cachedGCs.c revision 5104ee6e
1/* $XTermId: cachedGCs.c,v 1.84 2024/12/01 19:24:57 tom Exp $ */
2
3/*
4 * Copyright 2007-2021,2024 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) != NULL && (font)->fs != NULL)
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	CASE(Border);
94	CASE(Filler);
95#if OPT_BOX_CHARS || OPT_WIDE_CHARS
96	CASE(Line);
97	CASE(Dots);
98#endif
99#if OPT_DEC_CHRSET
100	CASE(CNorm);
101	CASE(CBold);
102#endif
103#if OPT_WIDE_CHARS
104	CASE(Wide);
105	CASE(WBold);
106	CASE(WideReverse);
107	CASE(WBoldReverse);
108#endif
109	CASE(VTcursNormal);
110	CASE(VTcursFilled);
111	CASE(VTcursReverse);
112	CASE(VTcursOutline);
113#if OPT_TEK4014
114	CASE(TKcurs);
115#endif
116	CASE(MAX);
117    }
118    return result;
119}
120
121#undef CASE
122
123static const char *
124traceVTwin(XtermWidget xw, const VTwin *value)
125{
126    const char *result = "?";
127    if (value == NULL)
128	result = "null";
129    else if (value == &(TScreenOf(xw)->fullVwin))
130	result = "fullVwin";
131#ifndef NO_ACTIVE_ICON
132    else if (value == &(TScreenOf(xw)->iconVwin))
133	result = "iconVwin";
134#endif
135    return result;
136}
137
138#if OPT_TRACE > 1
139static String
140traceCSet(unsigned cset)
141{
142    static char result[80];
143    switch (cset) {
144    case CSET_SWL:
145	strcpy(result, "SWL");
146	break;
147    case CSET_DHL_TOP:
148	strcpy(result, "DHL_TOP");
149	break;
150    case CSET_DHL_BOT:
151	strcpy(result, "DHL_BOT");
152	break;
153    case CSET_DWL:
154	strcpy(result, "DWL");
155	break;
156    default:
157	sprintf(result, "%#x", cset);
158	break;
159    }
160    return result;
161}
162
163static String
164traceFont(XTermFonts * font)
165{
166    static char result[80];
167
168    if (HaveFont(font)) {
169	XFontStruct *fs = font->fs;
170	sprintf(result, "%p(%dx%d %d %#lx)",
171		fs,
172		fs->max_bounds.width,
173		fs->max_bounds.ascent + fs->max_bounds.descent,
174		fs->max_bounds.descent,
175		(unsigned long) (fs->fid));
176    } else {
177	strcpy(result, "null");
178    }
179    return result;
180}
181
182static String
183tracePixel(XtermWidget xw, Pixel value)
184{
185#define CASE(name) { name, #name }
186    static struct {
187	TermColors code;
188	String name;
189    } t_colors[] = {
190	CASE(TEXT_FG),
191	    CASE(TEXT_BG),
192	    CASE(TEXT_CURSOR),
193	    CASE(MOUSE_FG),
194	    CASE(MOUSE_BG),
195#if OPT_TEK4014
196	    CASE(TEK_FG),
197	    CASE(TEK_BG),
198#endif
199#if OPT_HIGHLIGHT_COLOR
200	    CASE(HIGHLIGHT_BG),
201	    CASE(HIGHLIGHT_FG),
202#endif
203#if OPT_TEK4014
204	    CASE(TEK_CURSOR),
205#endif
206    };
207    TScreen *screen = TScreenOf(xw);
208    String result = 0;
209    int n;
210
211    for (n = 0; n < NCOLORS; ++n) {
212	if (value == T_COLOR(screen, t_colors[n].code)) {
213	    result = t_colors[n].name;
214	    break;
215	}
216    }
217
218    if (result == 0) {
219	for (n = 0; n < MAXCOLORS; ++n) {
220	    if (screen->Acolors[n].mode > 0
221		&& value == screen->Acolors[n].value) {
222		result = screen->Acolors[n].resource;
223		break;
224	    }
225	}
226    }
227
228    if (result == 0) {
229	char temp[80];
230	sprintf(temp, "%#lx", value);
231	result = x_strdup(temp);
232    }
233
234    return result;
235}
236
237#undef CASE
238
239#endif /* OPT_TRACE > 1 */
240#endif /* OPT_TRACE */
241
242static CgsCache *
243allocCache(void **cache_pointer)
244{
245    if (*cache_pointer == NULL) {
246	*cache_pointer = TypeCallocN(CgsCache, gcMAX);
247	TRACE(("allocCache %p\n", *cache_pointer));
248    }
249    return *((CgsCache **) cache_pointer);
250}
251
252#define ALLOC_CACHE(p) ((*(p) == NULL) ? allocCache(p) : *(p))
253
254static int
255dataIndex(CgsCache * me)
256{
257    return ITEM();
258}
259
260static void
261relinkData(CgsCache * me, int item)
262{
263    LINK(item);
264}
265
266/*
267 * Returns the appropriate cache pointer.
268 */
269static CgsCache *
270myCache(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
271{
272    CgsCache *result = NULL;
273
274    if ((int) cgsId >= 0 && cgsId < gcMAX) {
275#ifdef NO_ACTIVE_ICON
276	(void) xw;
277	(void) cgsWin;
278#else
279	if (cgsWin == &(TScreenOf(xw)->iconVwin))
280	    result = ALLOC_CACHE(&(TScreenOf(xw)->icon_cgs_cache));
281	else
282#endif
283	    result = ALLOC_CACHE(&(TScreenOf(xw)->main_cgs_cache));
284
285	result += cgsId;
286	if (result->data == NULL) {
287	    result->data = result->list;
288	}
289    }
290
291    return result;
292}
293
294static Display *
295myDisplay(XtermWidget xw)
296{
297    return TScreenOf(xw)->display;
298}
299
300static Drawable
301myDrawable(XtermWidget xw, VTwin *cgsWin)
302{
303    Drawable drawable = 0;
304
305    if (cgsWin != NULL && cgsWin->window != 0)
306	drawable = cgsWin->window;
307    if (drawable == 0)
308	drawable = RootWindowOfScreen(XtScreen(xw));
309    return drawable;
310}
311
312static GC
313newCache(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, CgsCache * me)
314{
315    XGCValues xgcv;
316    XtGCMask mask;
317
318    THIS(font) = NEXT(font);
319    THIS(cset) = NEXT(cset);
320    THIS(fg) = NEXT(fg);
321    THIS(bg) = NEXT(bg);
322
323    memset(&xgcv, 0, sizeof(xgcv));
324    xgcv.font = NEXT(font)->fs->fid;
325    mask = (GCForeground | GCBackground | GCFont);
326
327    switch (cgsId) {
328    case gcFiller:
329    case gcBorder:
330	mask &= (XtGCMask) ~ GCFont;
331	/* FALLTHRU */
332    case gcNorm:
333    case gcBold:
334    case gcNormReverse:
335    case gcBoldReverse:
336#if OPT_WIDE_CHARS
337    case gcWide:
338    case gcWBold:
339    case gcWideReverse:
340    case gcWBoldReverse:
341#endif
342	mask |= (GCGraphicsExposures | GCFunction);
343	xgcv.graphics_exposures = True;		/* default */
344	xgcv.function = GXcopy;
345	break;
346#if OPT_BOX_CHARS || OPT_WIDE_CHARS
347    case gcLine:
348	mask |= (GCGraphicsExposures | GCFunction);
349	xgcv.graphics_exposures = True;		/* default */
350	xgcv.function = GXcopy;
351	break;
352    case gcDots:
353	xgcv.fill_style = FillTiled;
354	xgcv.tile =
355	    XmuCreateStippledPixmap(XtScreen((Widget) xw),
356				    THIS(fg),
357				    THIS(bg),
358				    xw->core.depth);
359	THIS(tile) = xgcv.tile;
360	mask = (GCForeground | GCBackground);
361	mask |= (GCGraphicsExposures | GCFunction | GCTile | GCFillStyle);
362	xgcv.graphics_exposures = True;		/* default */
363	xgcv.function = GXcopy;
364	break;
365#endif
366#if OPT_DEC_CHRSET
367    case gcCNorm:
368    case gcCBold:
369	break;
370#endif
371    case gcVTcursNormal:	/* FALLTHRU */
372    case gcVTcursFilled:	/* FALLTHRU */
373    case gcVTcursReverse:	/* FALLTHRU */
374    case gcVTcursOutline:	/* FALLTHRU */
375	break;
376#if OPT_TEK4014
377    case gcTKcurs:		/* FALLTHRU */
378	/* FIXME */
379#endif
380    case gcMAX:		/* should not happen */
381	return NULL;
382    }
383    xgcv.foreground = NEXT(fg);
384    xgcv.background = NEXT(bg);
385
386    THIS(gc) = XCreateGC(myDisplay(xw), myDrawable(xw, cgsWin), mask, &xgcv);
387    TRACE(("getCgsGC(%s) created gc %p(%d)\n",
388	   traceCgsEnum(cgsId), (void *) THIS(gc), ITEM()));
389
390    THIS(used) = 0;
391    return THIS(gc);
392}
393
394#define SameFont(a, b) \
395	(Boolean) (HaveFont(a) \
396		   && HaveFont(b) \
397		   && (((a)->fs == (b)->fs) \
398		       || !memcmp((a)->fs, (b)->fs, sizeof(*((a)->fs)))))
399
400#define SameColor(a,b) ((a) == (b))
401#define SameCSet(a,b)  ((a) == (b))
402
403static GC
404chgCache(XtermWidget xw, CgsEnum cgsId GCC_UNUSED, CgsCache * me, Bool both)
405{
406    XGCValues xgcv;
407    XtGCMask mask = (GCForeground | GCBackground | GCFont);
408
409    memset(&xgcv, 0, sizeof(xgcv));
410
411    TRACE2(("chgCache(%s) old data fg=%s, bg=%s, font=%s cset %s\n",
412	    traceCgsEnum(cgsId),
413	    tracePixel(xw, THIS(fg)),
414	    tracePixel(xw, THIS(bg)),
415	    traceFont(THIS(font)),
416	    traceCSet(THIS(cset))));
417#if OPT_TRACE > 1
418    if (!SameFont(THIS(font), NEXT(font)))
419	TRACE2(("...chgCache new font=%s\n", traceFont(NEXT(font))));
420    if (!SameCSet(THIS(cset), NEXT(cset)))
421	TRACE2(("...chgCache new cset=%s\n", traceCSet(NEXT(cset))));
422    if (!SameColor(THIS(fg), NEXT(fg)))
423	TRACE2(("...chgCache new fg=%s\n", tracePixel(xw, NEXT(fg))));
424    if (!SameColor(THIS(bg), NEXT(bg)))
425	TRACE2(("...chgCache new bg=%s\n", tracePixel(xw, NEXT(bg))));
426#endif
427
428    if (both) {
429	THIS(font) = NEXT(font);
430	THIS(cset) = NEXT(cset);
431    }
432    THIS(fg) = NEXT(fg);
433    THIS(bg) = NEXT(bg);
434
435    xgcv.font = THIS(font)->fs->fid;
436    xgcv.foreground = THIS(fg);
437    xgcv.background = THIS(bg);
438
439    XChangeGC(myDisplay(xw), THIS(gc), mask, &xgcv);
440    TRACE2(("...chgCache(%s) updated gc %p(%d)\n",
441	    traceCgsEnum(cgsId), THIS(gc), ITEM()));
442
443    THIS(used) = 0;
444    return THIS(gc);
445}
446
447/*
448 * Use the "setCgsXXXX()" calls to initialize parameters for a new GC.
449 */
450void
451setCgsFore(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, Pixel fg)
452{
453    CgsCache *me;
454
455    if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
456	NEXT(fg) = fg;
457	me->mask |= GCForeground;
458	TRACE2(("setCgsFore(%s) %s\n",
459		traceCgsEnum(cgsId),
460		tracePixel(xw, NEXT(fg))));
461    }
462}
463
464void
465setCgsBack(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, Pixel bg)
466{
467    CgsCache *me;
468
469    if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
470	NEXT(bg) = bg;
471	me->mask |= GCBackground;
472	TRACE2(("setCgsBack(%s) %s\n",
473		traceCgsEnum(cgsId),
474		tracePixel(xw, NEXT(bg))));
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)) != NULL) {
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
494setCgsFont2(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, XTermFonts * font, unsigned which)
495{
496    CgsCache *me;
497
498    if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
499	TScreen *screen = TScreenOf(xw);
500	if (!HaveFont(font)) {
501	    if (cgsId != gcNorm)
502		(void) getCgsGC(xw, cgsWin, gcNorm);
503#ifndef NO_ACTIVE_ICON
504	    if (cgsWin == &(TScreenOf(xw)->iconVwin))
505		font = getIconicFont(screen);
506	    else
507#endif
508		font = GetNormalFont(screen, which);
509	}
510	if (HaveFont(font) && okFont(font->fs)) {
511	    TRACE2(("setCgsFont next: %s for %s slot %p, gc %p\n",
512		    traceFont(font), traceCgsEnum(cgsId),
513		    me, THIS(gc)));
514	    TRACE2(("...next font was %s\n", traceFont(NEXT(font))));
515	    NEXT(font) = font;
516	    me->mask |= GCFont;
517	} else {
518	    /* EMPTY */
519	    TRACE2(("...NOT updated font for %s\n",
520		    traceCgsEnum(cgsId)));
521	}
522    }
523}
524
525void
526setCgsFont(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, XTermFonts * font)
527{
528    setCgsFont2(xw, cgsWin, cgsId, font, fNorm);
529}
530
531/*
532 * Discard all of the font information, e.g., we are resizing the font.
533 * Keep the GC's so we can simply change them rather than creating new ones.
534 */
535void
536clrCgsFonts(XtermWidget xw, VTwin *cgsWin, XTermFonts * font)
537{
538    if (HaveFont(font)) {
539	int j;
540	for_each_gc(j) {
541	    CgsCache *me;
542	    if ((me = myCache(xw, cgsWin, (CgsEnum) j)) != NULL) {
543		int k;
544		for (k = 0; k < DEPTH; ++k) {
545		    if (SameFont(LIST(k).font, font)) {
546			TRACE2(("clrCgsFonts %s gc %p(%d) %s\n",
547				traceCgsEnum((CgsEnum) j),
548				LIST(k).gc,
549				k,
550				traceFont(font)));
551			LIST(k).font = NULL;
552			LIST(k).cset = 0;
553		    }
554		}
555		if (SameFont(NEXT(font), font)) {
556		    TRACE2(("clrCgsFonts %s next %s\n",
557			    traceCgsEnum((CgsEnum) j),
558			    traceFont(font)));
559		    NEXT(font) = NULL;
560		    NEXT(cset) = 0;
561		    me->mask &= (unsigned) ~(GCFont | GC_CSet);
562		}
563	    }
564	}
565    }
566}
567
568/*
569 * Return a GC associated with the given id, allocating if needed.
570 */
571GC
572getCgsGC(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
573{
574    CgsCache *me;
575    GC result = NULL;
576
577    if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
578	TRACE2(("getCgsGC(%s, %s)\n",
579		traceVTwin(xw, cgsWin), traceCgsEnum(cgsId)));
580	if (me->mask != 0) {
581	    int j;
582	    unsigned used = 0;
583
584	    /* fill in the unchanged fields */
585	    if (!(me->mask & GC_CSet))
586		NEXT(cset) = 0;	/* OPT_DEC_CHRSET */
587	    if (!(me->mask & GCFont))
588		NEXT(font) = THIS(font);
589	    if (!(me->mask & GCForeground))
590		NEXT(fg) = THIS(fg);
591	    if (!(me->mask & GCBackground))
592		NEXT(bg) = THIS(bg);
593
594	    if (NEXT(font) == NULL) {
595		setCgsFont(xw, cgsWin, cgsId, NULL);
596	    }
597
598	    TRACE2(("...Cgs new data fg=%s, bg=%s, font=%s cset %s\n",
599		    tracePixel(xw, NEXT(fg)),
600		    tracePixel(xw, NEXT(bg)),
601		    traceFont(NEXT(font)),
602		    traceCSet(NEXT(cset))));
603
604	    /* try to find the given data in an already-created GC */
605	    for (j = 0; j < DEPTH; ++j) {
606		if (LIST(j).gc != NULL
607		    && SameFont(LIST(j).font, NEXT(font))
608		    && SameCSet(LIST(j).cset, NEXT(cset))
609		    && SameColor(LIST(j).fg, NEXT(fg))
610		    && SameColor(LIST(j).bg, NEXT(bg))) {
611		    LINK(j);
612		    result = THIS(gc);
613		    TRACE2(("getCgsGC existing %p(%d)\n", result, ITEM()));
614		    break;
615		}
616	    }
617
618	    if (result == NULL) {
619		/* try to find an empty slot, to create a new GC */
620		used = 0;
621		for (j = 0; j < DEPTH; ++j) {
622		    if (LIST(j).gc == NULL) {
623			LINK(j);
624			result = newCache(xw, cgsWin, cgsId, me);
625			break;
626		    }
627		    if (used < LIST(j).used)
628			used = LIST(j).used;
629		}
630	    }
631
632	    if (result == NULL) {
633		int k;
634		/* if none were empty, pick the least-used slot, to modify */
635		for (j = 0, k = -1; j < DEPTH; ++j) {
636		    if (used >= LIST(j).used) {
637			used = LIST(j).used;
638			k = j;
639		    }
640		}
641		if (k >= 0) {
642		    LINK(k);
643		    TRACE2(("...getCgsGC least-used(%d) was %d\n", k, THIS(used)));
644		    result = chgCache(xw, cgsId, me, True);
645		}
646	    }
647	    me->next = *(me->data);
648	} else {
649	    result = THIS(gc);
650	}
651	me->mask = 0;
652	THIS(used) += 1;
653	TRACE2(("...getCgsGC(%s, %s) gc %p(%d), used %d\n",
654		traceVTwin(xw, cgsWin),
655		traceCgsEnum(cgsId), result, ITEM(), THIS(used)));
656    }
657    return result;
658}
659
660/*
661 * Return the font for the given GC.
662 */
663CgsEnum
664getCgsId(XtermWidget xw, VTwin *cgsWin, GC gc)
665{
666    int n;
667    CgsEnum result = gcNorm;
668
669    for_each_gc(n) {
670	CgsCache *me;
671
672	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
673	    if (THIS(gc) == gc) {
674		result = (CgsEnum) n;
675		break;
676	    }
677	}
678    }
679    return result;
680}
681
682/*
683 * Return the font for the given GC.
684 */
685XTermFonts *
686getCgsFont(XtermWidget xw, VTwin *cgsWin, GC gc)
687{
688    int n;
689    XTermFonts *result = NULL;
690
691    for_each_gc(n) {
692	CgsCache *me;
693
694	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
695	    if (THIS(gc) == gc) {
696		result = THIS(font);
697		break;
698	    }
699	}
700    }
701    return result;
702}
703
704/*
705 * Return the foreground color for the given GC.
706 */
707Pixel
708getCgsFore(XtermWidget xw, VTwin *cgsWin, GC gc)
709{
710    int n;
711    Pixel result = 0;
712
713    for_each_gc(n) {
714	CgsCache *me;
715
716	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
717	    if (THIS(gc) == gc) {
718		result = THIS(fg);
719		break;
720	    }
721	}
722    }
723    return result;
724}
725
726/*
727 * Return the background color for the given GC.
728 */
729Pixel
730getCgsBack(XtermWidget xw, VTwin *cgsWin, GC gc)
731{
732    int n;
733    Pixel result = 0;
734
735    for_each_gc(n) {
736	CgsCache *me;
737
738	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
739	    if (THIS(gc) == gc) {
740		result = THIS(bg);
741		break;
742	    }
743	}
744    }
745    return result;
746}
747
748/*
749 * Copy the parameters (except GC of course) from one cache record to another.
750 */
751void
752copyCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
753{
754    if (dstCgsId != srcCgsId) {
755	CgsCache *me;
756
757	if ((me = myCache(xw, cgsWin, srcCgsId)) != NULL) {
758	    TRACE(("copyCgs from %s to %s\n",
759		   traceCgsEnum(srcCgsId),
760		   traceCgsEnum(dstCgsId)));
761	    TRACE2(("copyCgs from %s (me %p, fg %s, bg %s, cset %s) to %s "
762		    TRACE_L "\n",
763		    traceCgsEnum(srcCgsId),
764		    me,
765		    tracePixel(xw, THIS(fg)),
766		    tracePixel(xw, THIS(bg)),
767		    traceCSet(THIS(cset)),
768		    traceCgsEnum(dstCgsId)));
769	    setCgsCSet(xw, cgsWin, dstCgsId, THIS(cset));
770	    setCgsFore(xw, cgsWin, dstCgsId, THIS(fg));
771	    setCgsBack(xw, cgsWin, dstCgsId, THIS(bg));
772	    setCgsFont(xw, cgsWin, dstCgsId, THIS(font));
773	    TRACE2(("...copyCgs " TRACE_R "\n"));
774	}
775    }
776}
777
778/*
779 * Interchange colors in the cache, e.g., for reverse-video.
780 */
781void
782redoCgs(XtermWidget xw, Pixel fg, Pixel bg, CgsEnum cgsId)
783{
784    VTwin *cgsWin = WhichVWin(TScreenOf(xw));
785    CgsCache *me = myCache(xw, cgsWin, cgsId);
786
787    if (me != NULL) {
788	CgsCacheData *save_data = me->data;
789	int n;
790
791	for (n = 0; n < DEPTH; ++n) {
792	    if (LIST(n).gc != NULL && HaveFont(LIST(n).font)) {
793		LINK(n);
794
795		if (LIST(n).fg == fg
796		    && LIST(n).bg == bg) {
797		    setCgsFore(xw, cgsWin, cgsId, bg);
798		    setCgsBack(xw, cgsWin, cgsId, fg);
799		} else if (LIST(n).fg == bg
800			   && LIST(n).bg == fg) {
801		    setCgsFore(xw, cgsWin, cgsId, fg);
802		    setCgsBack(xw, cgsWin, cgsId, bg);
803		} else {
804		    continue;
805		}
806
807		(void) chgCache(xw, cgsId, me, False);
808	    }
809	}
810	me->data = save_data;
811    }
812}
813
814/*
815 * Swap the cache records, e.g., when doing reverse-video.
816 */
817void
818swapCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
819{
820    if (dstCgsId != srcCgsId) {
821	CgsCache *src;
822
823	if ((src = myCache(xw, cgsWin, srcCgsId)) != NULL) {
824	    CgsCache *dst;
825
826	    if ((dst = myCache(xw, cgsWin, dstCgsId)) != NULL) {
827		CgsCache tmp;
828		int srcIndex = dataIndex(src);
829		int dstIndex = dataIndex(dst);
830
831		EXCHANGE(*src, *dst, tmp);
832
833		relinkData(src, dstIndex);
834		relinkData(dst, srcIndex);
835	    }
836	}
837    }
838}
839
840/*
841 * Free any GC associated with the given id.
842 */
843GC
844freeCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
845{
846    CgsCache *me;
847
848    if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
849	int j;
850
851	for (j = 0; j < DEPTH; ++j) {
852	    if (LIST(j).gc != NULL) {
853		TRACE(("freeCgs(%s, %s) gc %p(%d)\n",
854		       traceVTwin(xw, cgsWin),
855		       traceCgsEnum(cgsId), (void *) LIST(j).gc, j));
856		clrCgsFonts(xw, cgsWin, LIST(j).font);
857#if OPT_BOX_CHARS
858		if (cgsId == gcDots) {
859		    XmuReleaseStippledPixmap(XtScreen((Widget) xw), LIST(j).tile);
860		}
861#endif
862		XFreeGC(TScreenOf(xw)->display, LIST(j).gc);
863		memset(&LIST(j), 0, sizeof(LIST(j)));
864	    }
865	    LINK(0);
866	}
867    }
868    return NULL;
869}
870
871#ifdef NO_LEAKS
872void
873noleaks_cachedCgs(XtermWidget xw)
874{
875#ifndef NO_ACTIVE_ICON
876    free(TScreenOf(xw)->icon_cgs_cache);
877#endif
878    free(TScreenOf(xw)->main_cgs_cache);
879}
880#endif
881