Home | History | Annotate | Line # | Download | only in dist
      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  */
     54 typedef 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 
     75 typedef 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
     84 static const char *
     85 traceCgsEnum(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 
    123 static const char *
    124 traceVTwin(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
    139 static String
    140 traceCSet(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 
    163 static String
    164 traceFont(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 
    182 static String
    183 tracePixel(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 
    242 static CgsCache *
    243 allocCache(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 
    254 static int
    255 dataIndex(CgsCache * me)
    256 {
    257     return ITEM();
    258 }
    259 
    260 static void
    261 relinkData(CgsCache * me, int item)
    262 {
    263     LINK(item);
    264 }
    265 
    266 /*
    267  * Returns the appropriate cache pointer.
    268  */
    269 static CgsCache *
    270 myCache(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 
    294 static Display *
    295 myDisplay(XtermWidget xw)
    296 {
    297     return TScreenOf(xw)->display;
    298 }
    299 
    300 static Drawable
    301 myDrawable(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 
    312 static GC
    313 newCache(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 
    403 static GC
    404 chgCache(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  */
    450 void
    451 setCgsFore(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 
    464 void
    465 setCgsBack(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
    479 void
    480 setCgsCSet(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 
    493 void
    494 setCgsFont2(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 
    525 void
    526 setCgsFont(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  */
    535 void
    536 clrCgsFonts(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  */
    571 GC
    572 getCgsGC(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  */
    663 CgsEnum
    664 getCgsId(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  */
    685 XTermFonts *
    686 getCgsFont(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  */
    707 Pixel
    708 getCgsFore(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  */
    729 Pixel
    730 getCgsBack(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  */
    751 void
    752 copyCgs(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  */
    781 void
    782 redoCgs(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  */
    817 void
    818 swapCgs(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  */
    843 GC
    844 freeCgs(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
    872 void
    873 noleaks_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