#include <data.h>
#include <xstrings.h>
#include <X11/Xmu/Drawing.h>
#include <stdio.h>
typedef struct {
GC gc;
unsigned used;
unsigned cset;
XTermFonts *font;
Pixel tile;
Pixel fg;
Pixel bg;
} CgsCacheData;
#define DEPTH 8
#define ITEM() (me->data - me->list)
#define LIST(item) me->list[item]
#define LINK(item) me->data = (me->list + (item))
#define THIS(field) me->data->field
#define NEXT(field) me->next.field
#define GC_CSet GCFunction
typedef struct {
CgsCacheData list[DEPTH];
CgsCacheData *data;
XtGCMask mask;
CgsCacheData next;
} CgsCache;
#if OPT_TRACE
#define CASE(name) case gc##name: result = #name; break
static String
traceCgsEnum(CgsEnum value)
{
String result = "?";
switch (value) {
CASE(Norm);
CASE(Bold);
CASE(NormReverse);
CASE(BoldReverse);
#if OPT_BOX_CHARS
CASE(Line);
CASE(Dots);
#endif
#if OPT_DEC_CHRSET
CASE(CNorm);
CASE(CBold);
#endif
#if OPT_WIDE_CHARS
CASE(Wide);
CASE(WBold);
CASE(WideReverse);
CASE(WBoldReverse);
#endif
CASE(VTcursNormal);
CASE(VTcursFilled);
CASE(VTcursReverse);
CASE(VTcursOutline);
#if OPT_TEK4014
CASE(TKcurs);
#endif
CASE(MAX);
}
return result;
}
#undef CASE
static String
traceVTwin(XtermWidget xw, VTwin * value)
{
String result = "?";
if (value == 0)
result = "null";
else if (value == &(xw->screen.fullVwin))
result = "fullVwin";
#ifndef NO_ACTIVE_ICON
else if (value == &(xw->screen.iconVwin))
result = "iconVwin";
#endif
return result;
}
#if OPT_TRACE > 1
static String
traceCSet(unsigned cset)
{
static char result[80];
switch (cset) {
case CSET_SWL:
strcpy(result, "SWL");
break;
case CSET_DHL_TOP:
strcpy(result, "DHL_TOP");
break;
case CSET_DHL_BOT:
strcpy(result, "DHL_BOT");
break;
case CSET_DWL:
strcpy(result, "DWL");
break;
default:
sprintf(result, "%#x", cset);
break;
}
return result;
}
static String
traceFont(XTermFonts * font)
{
static char result[80];
XFontStruct *fs;
if (font != 0 && (fs = font->fs) != 0) {
sprintf(result, "%p(%dx%d %d %#lx)",
fs,
fs->max_bounds.width,
fs->max_bounds.ascent + fs->max_bounds.descent,
fs->max_bounds.descent,
(unsigned long) (fs->fid));
} else {
strcpy(result, "null");
}
return result;
}
static String
tracePixel(XtermWidget xw, Pixel value)
{
#define CASE(name) { name, #name }
static struct {
TermColors code;
String name;
} t_colors[] = {
CASE(TEXT_FG),
CASE(TEXT_BG),
CASE(TEXT_CURSOR),
CASE(MOUSE_FG),
CASE(MOUSE_BG),
#if OPT_TEK4014
CASE(TEK_FG),
CASE(TEK_BG),
#endif
#if OPT_HIGHLIGHT_COLOR
CASE(HIGHLIGHT_BG),
CASE(HIGHLIGHT_FG),
#endif
#if OPT_TEK4014
CASE(TEK_CURSOR),
#endif
};
TScreen *screen = &(xw->screen);
String result = 0;
int n;
for (n = 0; n < NCOLORS; ++n) {
if (value == T_COLOR(screen, t_colors[n].code)) {
result = t_colors[n].name;
break;
}
}
if (result == 0) {
for (n = 0; n < MAXCOLORS; ++n) {
#if OPT_COLOR_RES
if (screen->Acolors[n].mode > 0
&& value == screen->Acolors[n].value) {
result = screen->Acolors[n].resource;
break;
}
#else
if (value == screen->Acolors[n]) {
char temp[80];
sprintf(temp, "Acolors[%d]", n);
result = x_strdup(temp);
break;
}
#endif
}
}
if (result == 0) {
char temp[80];
sprintf(temp, "%#lx", value);
result = x_strdup(temp);
}
return result;
}
#undef CASE
#endif
#endif
static CgsCache *
allocCache(void **cache_pointer)
{
if (*cache_pointer == 0) {
*cache_pointer = TypeCallocN(CgsCache, gcMAX);
TRACE(("allocCache %p\n", cache_pointer));
}
return *((CgsCache **) cache_pointer);
}
static int
dataIndex(CgsCache * me)
{
return ITEM();
}
static void
relinkData(CgsCache * me, int item)
{
LINK(item);
}
static CgsCache *
myCache(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
{
CgsCache *result = 0;
if ((int) cgsId >= 0 && cgsId < gcMAX) {
#ifdef NO_ACTIVE_ICON
(void) xw;
(void) cgsWin;
#else
if (cgsWin == &(xw->screen.iconVwin))
result = allocCache(&(xw->screen.icon_cgs_cache));
else
#endif
result = allocCache(&(xw->screen.main_cgs_cache));
result += cgsId;
if (result->data == 0) {
result->data = result->list;
}
}
return result;
}
static Display *
myDisplay(XtermWidget xw)
{
return xw->screen.display;
}
static Drawable
myDrawable(XtermWidget xw, VTwin * cgsWin)
{
Drawable drawable = 0;
if (cgsWin != 0 && cgsWin->window != 0)
drawable = cgsWin->window;
if (drawable == 0)
drawable = RootWindowOfScreen(XtScreen(xw));
return drawable;
}
static GC
newCache(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, CgsCache * me)
{
XGCValues xgcv;
XtGCMask mask;
THIS(font) = NEXT(font);
THIS(cset) = NEXT(cset);
THIS(fg) = NEXT(fg);
THIS(bg) = NEXT(bg);
memset(&xgcv, 0, sizeof(xgcv));
xgcv.font = NEXT(font)->fs->fid;
mask = (GCForeground | GCBackground | GCFont);
switch (cgsId) {
case gcNorm:
case gcBold:
case gcNormReverse:
case gcBoldReverse:
#if OPT_WIDE_CHARS
case gcWide:
case gcWBold:
case gcWideReverse:
case gcWBoldReverse:
#endif
mask |= (GCGraphicsExposures | GCFunction);
xgcv.graphics_exposures = True;
xgcv.function = GXcopy;
break;
#if OPT_BOX_CHARS
case gcLine:
mask |= (GCGraphicsExposures | GCFunction);
xgcv.graphics_exposures = True;
xgcv.function = GXcopy;
break;
case gcDots:
xgcv.fill_style = FillTiled;
xgcv.tile =
XmuCreateStippledPixmap(XtScreen((Widget) xw),
THIS(fg),
THIS(bg),
xw->core.depth);
THIS(tile) = xgcv.tile;
mask = (GCForeground | GCBackground);
mask |= (GCGraphicsExposures | GCFunction | GCTile | GCFillStyle);
xgcv.graphics_exposures = True;
xgcv.function = GXcopy;
break;
#endif
#if OPT_DEC_CHRSET
case gcCNorm:
case gcCBold:
break;
#endif
case gcVTcursNormal:
case gcVTcursFilled:
case gcVTcursReverse:
case gcVTcursOutline:
break;
#if OPT_TEK4014
case gcTKcurs:
#endif
case gcMAX:
return 0;
}
xgcv.foreground = NEXT(fg);
xgcv.background = NEXT(bg);
THIS(gc) = XCreateGC(myDisplay(xw), myDrawable(xw, cgsWin), mask, &xgcv);
TRACE(("getCgsGC(%s) created gc %p(%d)\n",
traceCgsEnum(cgsId), THIS(gc), ITEM()));
THIS(used) = 0;
return THIS(gc);
}
static Boolean
HaveFont(XTermFonts * a)
{
return (Boolean) (a != 0 && a->fs != 0);
}
static Boolean
SameFont(XTermFonts * a, XTermFonts * b)
{
return (Boolean) (HaveFont(a)
&& HaveFont(b)
&& ((a->fs == b->fs)
|| !memcmp(a->fs, b->fs, sizeof(*(a->fs)))));
}
#define SameColor(a,b) ((a) == (b))
#define SameCSet(a,b) ((a) == (b))
static GC
chgCache(XtermWidget xw, CgsEnum cgsId GCC_UNUSED, CgsCache * me)
{
XGCValues xgcv;
XtGCMask mask = (GCForeground | GCBackground | GCFont);
memset(&xgcv, 0, sizeof(xgcv));
TRACE2(("chgCache(%s) old data fg=%s, bg=%s, font=%s cset %s\n",
traceCgsEnum(cgsId),
tracePixel(xw, THIS(fg)),
tracePixel(xw, THIS(bg)),
traceFont(THIS(font)),
traceCSet(THIS(cset))));
#if OPT_TRACE > 1
if (!SameFont(THIS(font), NEXT(font)))
TRACE2(("...chgCache new font=%s\n", traceFont(NEXT(font))));
if (!SameCSet(THIS(cset), NEXT(cset)))
TRACE2(("...chgCache new cset=%s\n", traceCSet(NEXT(cset))));
if (!SameColor(THIS(fg), NEXT(fg)))
TRACE2(("...chgCache new fg=%s\n", tracePixel(xw, NEXT(fg))));
if (!SameColor(THIS(bg), NEXT(bg)))
TRACE2(("...chgCache new bg=%s\n", tracePixel(xw, NEXT(bg))));
#endif
THIS(font) = NEXT(font);
THIS(cset) = NEXT(cset);
THIS(fg) = NEXT(fg);
THIS(bg) = NEXT(bg);
xgcv.font = THIS(font)->fs->fid;
xgcv.foreground = THIS(fg);
xgcv.background = THIS(bg);
XChangeGC(myDisplay(xw), THIS(gc), mask, &xgcv);
TRACE2(("...chgCache(%s) updated gc %p(%d)\n",
traceCgsEnum(cgsId), THIS(gc), ITEM()));
THIS(used) = 0;
return THIS(gc);
}
void
setCgsFore(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, Pixel fg)
{
CgsCache *me;
if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
if (!SameColor(NEXT(fg), fg)) {
NEXT(fg) = fg;
me->mask |= GCForeground;
}
}
}
void
setCgsBack(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, Pixel bg)
{
CgsCache *me;
if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
if (!SameColor(NEXT(bg), bg)) {
NEXT(bg) = bg;
me->mask |= GCBackground;
}
}
}
#if OPT_DEC_CHRSET
void
setCgsCSet(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, unsigned cset)
{
CgsCache *me;
if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
if (!SameCSet(NEXT(cset), cset)) {
NEXT(cset) = cset;
me->mask |= GC_CSet;
}
}
}
#else
#define setCgsCSet(xw, cgsWin, dstCgsId, cset)
#endif
void
setCgsFont(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, XTermFonts * font)
{
CgsCache *me;
if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
if (!HaveFont(font)) {
if (cgsId != gcNorm)
(void) getCgsGC(xw, cgsWin, gcNorm);
#ifndef NO_ACTIVE_ICON
if (cgsWin == &(xw->screen.iconVwin))
font = &(xw->screen.fnt_icon);
else
#endif
font = &(xw->screen.fnts[fNorm]);
}
if (okFont(font->fs) && !SameFont(NEXT(font), font)) {
TRACE2(("...updated next font for %s to %s\n",
traceCgsEnum(cgsId), traceFont(font)));
TRACE2(("...next font was %s\n", traceFont(NEXT(font))));
NEXT(font) = font;
me->mask |= GCFont;
} else {
TRACE2(("...NOT updated font for %s\n",
traceCgsEnum(cgsId)));
}
}
}
void
clrCgsFonts(XtermWidget xw, VTwin * cgsWin, XTermFonts * font)
{
CgsCache *me;
int j, k;
if (HaveFont(font)) {
for_each_gc(j) {
if ((me = myCache(xw, cgsWin, (CgsEnum) j)) != 0) {
for (k = 0; k < DEPTH; ++k) {
if (SameFont(LIST(k).font, font)) {
TRACE2(("clrCgsFonts %s gc %p(%d) %s\n",
traceCgsEnum((CgsEnum) j),
LIST(k).gc,
k,
traceFont(font)));
LIST(k).font = 0;
LIST(k).cset = 0;
}
}
if (SameFont(NEXT(font), font)) {
TRACE2(("clrCgsFonts %s next %s\n",
traceCgsEnum((CgsEnum) j),
traceFont(font)));
NEXT(font) = 0;
NEXT(cset) = 0;
me->mask &= ~(GCFont | GC_CSet);
}
}
}
}
}
GC
getCgsGC(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
{
CgsCache *me;
GC result = 0;
int j, k;
unsigned used = 0;
if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
TRACE2(("getCgsGC(%s, %s)\n",
traceVTwin(xw, cgsWin), traceCgsEnum(cgsId)));
if (me->mask != 0) {
if (!(me->mask & GC_CSet))
NEXT(cset) = 0;
if (!(me->mask & GCFont))
NEXT(font) = THIS(font);
if (!(me->mask & GCForeground))
NEXT(fg) = THIS(fg);
if (!(me->mask & GCBackground))
NEXT(bg) = THIS(bg);
if (NEXT(font) == 0) {
setCgsFont(xw, cgsWin, cgsId, 0);
}
TRACE2(("...Cgs new data fg=%s, bg=%s, font=%s cset %s\n",
tracePixel(xw, NEXT(fg)),
tracePixel(xw, NEXT(bg)),
traceFont(NEXT(font)),
traceCSet(NEXT(cset))));
for (j = 0; j < DEPTH; ++j) {
if (LIST(j).gc != 0
&& SameFont(LIST(j).font, NEXT(font))
&& SameCSet(LIST(j).cset, NEXT(cset))
&& SameColor(LIST(j).fg, NEXT(fg))
&& SameColor(LIST(j).bg, NEXT(bg))) {
LINK(j);
result = THIS(gc);
TRACE2(("getCgsGC existing %p(%d)\n", result, ITEM()));
break;
}
}
if (result == 0) {
used = 0;
for (j = 0; j < DEPTH; ++j) {
if (LIST(j).gc == 0) {
LINK(j);
result = newCache(xw, cgsWin, cgsId, me);
break;
}
if (used < LIST(j).used)
used = LIST(j).used;
}
}
if (result == 0) {
for (j = 0, k = -1; j < DEPTH; ++j) {
if (used >= LIST(j).used) {
used = LIST(j).used;
k = j;
}
}
LINK(k);
TRACE(("...getCgsGC least-used(%d) was %d\n", k, THIS(used)));
result = chgCache(xw, cgsId, me);
}
me->next = *(me->data);
} else {
result = THIS(gc);
}
me->mask = 0;
THIS(used) += 1;
TRACE2(("...getCgsGC(%s, %s) gc %p(%d), used %d\n",
traceVTwin(xw, cgsWin),
traceCgsEnum(cgsId), result, ITEM(), THIS(used)));
}
return result;
}
CgsEnum
getCgsId(XtermWidget xw, VTwin * cgsWin, GC gc)
{
int n;
CgsEnum result = gcNorm;
for_each_gc(n) {
CgsCache *me;
if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
if (THIS(gc) == gc) {
result = (CgsEnum) n;
break;
}
}
}
return result;
}
XTermFonts *
getCgsFont(XtermWidget xw, VTwin * cgsWin, GC gc)
{
int n;
XTermFonts *result = 0;
for_each_gc(n) {
CgsCache *me;
if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
if (THIS(gc) == gc) {
result = THIS(font);
break;
}
}
}
return result;
}
Pixel
getCgsFore(XtermWidget xw, VTwin * cgsWin, GC gc)
{
int n;
Pixel result = 0;
for_each_gc(n) {
CgsCache *me;
if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
if (THIS(gc) == gc) {
result = THIS(fg);
break;
}
}
}
return result;
}
Pixel
getCgsBack(XtermWidget xw, VTwin * cgsWin, GC gc)
{
int n;
Pixel result = 0;
for_each_gc(n) {
CgsCache *me;
if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
if (THIS(gc) == gc) {
result = THIS(bg);
break;
}
}
}
return result;
}
void
copyCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
{
if (dstCgsId != srcCgsId) {
CgsCache *me;
if ((me = myCache(xw, cgsWin, srcCgsId)) != 0) {
TRACE(("copyCgs from %s to %s\n",
traceCgsEnum(srcCgsId),
traceCgsEnum(dstCgsId)));
setCgsFont(xw, cgsWin, dstCgsId, THIS(font));
setCgsCSet(xw, cgsWin, dstCgsId, THIS(cset));
setCgsFore(xw, cgsWin, dstCgsId, THIS(fg));
setCgsBack(xw, cgsWin, dstCgsId, THIS(bg));
}
}
}
void
redoCgs(XtermWidget xw, Pixel fg, Pixel bg, CgsEnum cgsId)
{
int n;
VTwin *cgsWin = WhichVWin(&(xw->screen));
CgsCache *me = myCache(xw, cgsWin, cgsId);
if (me != 0) {
CgsCacheData *save_data = me->data;
for (n = 0; n < DEPTH; ++n) {
if (LIST(n).gc != 0) {
LINK(n);
if (LIST(n).fg == fg
&& LIST(n).bg == bg) {
setCgsFore(xw, cgsWin, cgsId, bg);
setCgsBack(xw, cgsWin, cgsId, fg);
} else if (LIST(n).fg == bg
&& LIST(n).bg == fg) {
setCgsFore(xw, cgsWin, cgsId, fg);
setCgsBack(xw, cgsWin, cgsId, bg);
} else {
continue;
}
(void) chgCache(xw, cgsId, me);
}
}
me->data = save_data;
}
}
void
swapCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
{
if (dstCgsId != srcCgsId) {
CgsCache *dst;
CgsCache *src;
CgsCache tmp;
if ((src = myCache(xw, cgsWin, srcCgsId)) != 0) {
if ((dst = myCache(xw, cgsWin, dstCgsId)) != 0) {
int srcIndex = dataIndex(src);
int dstIndex = dataIndex(dst);
tmp = *dst;
*dst = *src;
*src = tmp;
relinkData(src, dstIndex);
relinkData(dst, srcIndex);
}
}
}
}
GC
freeCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
{
CgsCache *me;
int j;
if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
for (j = 0; j < DEPTH; ++j) {
if (LIST(j).gc != 0) {
TRACE(("freeCgs(%s, %s) gc %p(%d)\n",
traceVTwin(xw, cgsWin),
traceCgsEnum(cgsId), LIST(j).gc, j));
clrCgsFonts(xw, cgsWin, LIST(j).font);
#if OPT_BOX_CHARS
if (cgsId == gcDots) {
XmuReleaseStippledPixmap(XtScreen((Widget) xw), LIST(j).tile);
}
#endif
XFreeGC(xw->screen.display, LIST(j).gc);
memset(&LIST(j), 0, sizeof(LIST(j)));
}
LINK(0);
}
}
return 0;
}
#ifdef NO_LEAKS
void
noleaks_cachedCgs(XtermWidget xw)
{
#ifndef NO_ACTIVE_ICON
free(xw->screen.icon_cgs_cache);
#endif
free(xw->screen.main_cgs_cache);
}
#endif