1 /* $XTermId: cachedGCs.c,v 1.81 2021/09/16 19:48:02 tom Exp $ */
2 
3 /*
4  * Copyright 2007-2019,2021 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) != 0 && (font)->fs != 0)
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 *
traceCgsEnum(CgsEnum value)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
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 *
traceVTwin(XtermWidget xw,VTwin * value)124 traceVTwin(XtermWidget xw, VTwin *value)
125 {
126     const char *result = "?";
127     if (value == 0)
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
traceCSet(unsigned cset)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
traceFont(XTermFonts * font)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
tracePixel(XtermWidget xw,Pixel value)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 *
allocCache(void ** cache_pointer)243 allocCache(void **cache_pointer)
244 {
245     if (*cache_pointer == 0) {
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) == 0) ? allocCache(p) : *(p))
253 
254 static int
dataIndex(CgsCache * me)255 dataIndex(CgsCache * me)
256 {
257     return ITEM();
258 }
259 
260 static void
relinkData(CgsCache * me,int item)261 relinkData(CgsCache * me, int item)
262 {
263     LINK(item);
264 }
265 
266 /*
267  * Returns the appropriate cache pointer.
268  */
269 static CgsCache *
myCache(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId)270 myCache(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
271 {
272     CgsCache *result = 0;
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 == 0) {
287 	    result->data = result->list;
288 	}
289     }
290 
291     return result;
292 }
293 
294 static Display *
myDisplay(XtermWidget xw)295 myDisplay(XtermWidget xw)
296 {
297     return TScreenOf(xw)->display;
298 }
299 
300 static Drawable
myDrawable(XtermWidget xw,VTwin * cgsWin)301 myDrawable(XtermWidget xw, VTwin *cgsWin)
302 {
303     Drawable drawable = 0;
304 
305     if (cgsWin != 0 && cgsWin->window != 0)
306 	drawable = cgsWin->window;
307     if (drawable == 0)
308 	drawable = RootWindowOfScreen(XtScreen(xw));
309     return drawable;
310 }
311 
312 static GC
newCache(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId,CgsCache * me)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
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 0;
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
chgCache(XtermWidget xw,CgsEnum cgsId GCC_UNUSED,CgsCache * me,Bool both)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
setCgsFore(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId,Pixel fg)451 setCgsFore(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, Pixel fg)
452 {
453     CgsCache *me;
454 
455     if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
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
setCgsBack(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId,Pixel bg)465 setCgsBack(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, Pixel bg)
466 {
467     CgsCache *me;
468 
469     if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
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
setCgsCSet(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId,unsigned cset)480 setCgsCSet(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 
493 void
setCgsFont2(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId,XTermFonts * font,unsigned which)494 setCgsFont2(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, XTermFonts * font, unsigned which)
495 {
496     CgsCache *me;
497 
498     if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
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
setCgsFont(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId,XTermFonts * font)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
clrCgsFonts(XtermWidget xw,VTwin * cgsWin,XTermFonts * font)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)) != 0) {
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 = 0;
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) = 0;
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
getCgsGC(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId)572 getCgsGC(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
573 {
574     CgsCache *me;
575     GC result = 0;
576 
577     if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
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) == 0) {
595 		setCgsFont(xw, cgsWin, cgsId, 0);
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 != 0
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 == 0) {
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 == 0) {
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 == 0) {
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
getCgsId(XtermWidget xw,VTwin * cgsWin,GC gc)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)) != 0) {
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 *
getCgsFont(XtermWidget xw,VTwin * cgsWin,GC gc)686 getCgsFont(XtermWidget xw, VTwin *cgsWin, GC gc)
687 {
688     int n;
689     XTermFonts *result = 0;
690 
691     for_each_gc(n) {
692 	CgsCache *me;
693 
694 	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
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
getCgsFore(XtermWidget xw,VTwin * cgsWin,GC gc)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)) != 0) {
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
getCgsBack(XtermWidget xw,VTwin * cgsWin,GC gc)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)) != 0) {
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
copyCgs(XtermWidget xw,VTwin * cgsWin,CgsEnum dstCgsId,CgsEnum srcCgsId)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)) != 0) {
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
redoCgs(XtermWidget xw,Pixel fg,Pixel bg,CgsEnum cgsId)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 != 0) {
788 	CgsCacheData *save_data = me->data;
789 	int n;
790 
791 	for (n = 0; n < DEPTH; ++n) {
792 	    if (LIST(n).gc != 0 && 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
swapCgs(XtermWidget xw,VTwin * cgsWin,CgsEnum dstCgsId,CgsEnum srcCgsId)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)) != 0) {
824 	    CgsCache *dst;
825 
826 	    if ((dst = myCache(xw, cgsWin, dstCgsId)) != 0) {
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
freeCgs(XtermWidget xw,VTwin * cgsWin,CgsEnum cgsId)844 freeCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
845 {
846     CgsCache *me;
847 
848     if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
849 	int j;
850 
851 	for (j = 0; j < DEPTH; ++j) {
852 	    if (LIST(j).gc != 0) {
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 0;
869 }
870 
871 #ifdef NO_LEAKS
872 void
noleaks_cachedCgs(XtermWidget xw)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