xenocara/app/xterm/cachedGCs.c

868 lines
19 KiB
C

/* $XTermId: cachedGCs.c,v 1.48 2008/02/20 20:54:54 Julien.Cristau Exp $ */
/************************************************************
Copyright 2007,2008 by Thomas E. Dickey
All Rights Reserved
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name(s) of the above copyright
holders shall not be used in advertising or otherwise to promote the
sale, use or other dealings in this Software without prior written
authorization.
********************************************************/
#include <data.h>
#include <xstrings.h>
#include <X11/Xmu/Drawing.h>
#include <stdio.h>
/*
* hide (or eliminate) calls to
* XCreateGC()
* XFreeGC()
* XGetGCValues()
* XSetBackground()
* XSetFont()
* XSetForeground()
* XtGetGC()
* XtReleaseGC()
* by associating an integer with each GC, maintaining a cache which
* reflects frequency of use rather than most recent usage.
*
* FIXME: XTermFonts should hold gc, font, fs.
*/
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; /* points to current list[] entry */
XtGCMask mask; /* changes since the last getCgsGC() */
CgsCacheData next; /* updated values, apply in getCgsGC() */
} 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 /* OPT_TRACE > 1 */
#endif /* OPT_TRACE */
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);
}
/*
* Returns the appropriate cache pointer.
*/
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; /* default */
xgcv.function = GXcopy;
break;
#if OPT_BOX_CHARS
case gcLine:
mask |= (GCGraphicsExposures | GCFunction);
xgcv.graphics_exposures = True; /* default */
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; /* default */
xgcv.function = GXcopy;
break;
#endif
#if OPT_DEC_CHRSET
case gcCNorm:
case gcCBold:
break;
#endif
case gcVTcursNormal: /* FALLTHRU */
case gcVTcursFilled: /* FALLTHRU */
case gcVTcursReverse: /* FALLTHRU */
case gcVTcursOutline: /* FALLTHRU */
break;
#if OPT_TEK4014
case gcTKcurs: /* FALLTHRU */
/* FIXME */
#endif
case gcMAX: /* should not happen */
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 (a != 0 && a->fs != 0);
}
static Boolean
SameFont(XTermFonts * a, XTermFonts * b)
{
return (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);
}
/*
* Use the "setCgsXXXX()" calls to initialize parameters for a new 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) /* nothing */
#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)));
}
}
}
/*
* Discard all of the font information, e.g., we are resizing the font.
* Keep the GC's so we can simply change them rather than creating new ones.
*/
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);
}
}
}
}
}
/*
* Return a GC associated with the given id, allocating if needed.
*/
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) {
/* fill in the unchanged fields */
if (!(me->mask & GC_CSet))
NEXT(cset) = 0; /* OPT_DEC_CHRSET */
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))));
/* try to find the given data in an already-created GC */
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) {
/* try to find an empty slot, to create a new GC */
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) {
/* if none were empty, pick the least-used slot, to modify */
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;
}
/*
* Return the font for the given GC.
*/
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;
}
/*
* Return the font for the given GC.
*/
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;
}
/*
* Return the foreground color for the given GC.
*/
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;
}
/*
* Return the background color for the given GC.
*/
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;
}
/*
* Copy the parameters (except GC of course) from one cache record to another.
*/
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));
}
}
}
/*
* Interchange colors in the cache, e.g., for reverse-video.
*/
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;
}
}
/*
* Swap the cache records, e.g., when doing reverse-video.
*/
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);
}
}
}
}
/*
* Free any GC associated with the given id.
*/
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