summaryrefslogtreecommitdiff
path: root/fontutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'fontutils.c')
-rw-r--r--fontutils.c3197
1 files changed, 3197 insertions, 0 deletions
diff --git a/fontutils.c b/fontutils.c
new file mode 100644
index 0000000..88ad585
--- /dev/null
+++ b/fontutils.c
@@ -0,0 +1,3197 @@
+/* $XTermId: fontutils.c,v 1.380 2011/12/27 10:20:50 tom Exp $ */
+
+/*
+ * Copyright 1998-2010,2011 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.
+ */
+
+/*
+ * A portion of this module (for FontNameProperties) was adapted from EMU 1.3;
+ * it constructs font names with specific properties changed, e.g., for bold
+ * and double-size characters.
+ */
+
+#define RES_OFFSET(field) XtOffsetOf(SubResourceRec, field)
+
+#include <fontutils.h>
+#include <X11/Xmu/Drawing.h>
+#include <X11/Xmu/CharSet.h>
+
+#include <main.h>
+#include <data.h>
+#include <menu.h>
+#include <xstrings.h>
+#include <xterm.h>
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define SetFontWidth(screen,dst,src) (dst)->f_width = (src)
+#define SetFontHeight(screen,dst,src) (dst)->f_height = dimRound((screen)->scale_height * (float) (src))
+
+/* from X11/Xlibint.h - not all vendors install this file */
+#define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
+ (((cs)->rbearing|(cs)->lbearing| \
+ (cs)->ascent|(cs)->descent) == 0))
+
+#define CI_GET_CHAR_INFO_1D(fs,col,def,cs) \
+{ \
+ cs = def; \
+ if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
+ if (fs->per_char == NULL) { \
+ cs = &fs->min_bounds; \
+ } else { \
+ cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
+ if (CI_NONEXISTCHAR(cs)) cs = def; \
+ } \
+ } \
+}
+
+#define CI_GET_CHAR_INFO_2D(fs,row,col,def,cs) \
+{ \
+ cs = def; \
+ if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
+ col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
+ if (fs->per_char == NULL) { \
+ cs = &fs->min_bounds; \
+ } else { \
+ cs = &fs->per_char[((row - fs->min_byte1) * \
+ (fs->max_char_or_byte2 - \
+ fs->min_char_or_byte2 + 1)) + \
+ (col - fs->min_char_or_byte2)]; \
+ if (CI_NONEXISTCHAR(cs)) cs = def; \
+ } \
+ } \
+}
+
+#define MAX_FONTNAME 200
+
+/*
+ * A structure to hold the relevant properties from a font
+ * we need to make a well formed font name for it.
+ */
+typedef struct {
+ /* registry, foundry, family */
+ char *beginning;
+ /* weight */
+ char *weight;
+ /* slant */
+ char *slant;
+ /* wideness */
+ char *wideness;
+ /* add style */
+ char *add_style;
+ int pixel_size;
+ char *point_size;
+ int res_x;
+ int res_y;
+ char *spacing;
+ int average_width;
+ /* charset registry, charset encoding */
+ char *end;
+} FontNameProperties;
+
+#if OPT_RENDERFONT
+static void fillInFaceSize(XtermWidget, int);
+#endif
+
+#if OPT_SHIFT_FONTS
+static int lookupOneFontSize(XtermWidget, int);
+#endif
+
+#if OPT_WIDE_CHARS
+static unsigned
+countGlyphs(XFontStruct * fp)
+{
+ unsigned count = 0;
+
+ if (fp != 0) {
+ if (fp->min_byte1 == 0 && fp->max_byte1 == 0) {
+ count = fp->max_char_or_byte2 - fp->min_char_or_byte2;
+ } else if (fp->min_char_or_byte2 < 256
+ && fp->max_char_or_byte2 < 256) {
+ unsigned first = (fp->min_byte1 << 8) + fp->min_char_or_byte2;
+ unsigned last = (fp->max_byte1 << 8) + fp->max_char_or_byte2;
+ count = last + 1 - first;
+ }
+ }
+ return count;
+}
+
+/*
+ * Verify that the wide-bold font is at least a bold font with roughly as many
+ * glyphs as the wide font. The counts should be the same, but settle for
+ * filtering out the worst of the font mismatches.
+ */
+static Bool
+compatibleWideCounts(XFontStruct * wfs, XFontStruct * wbfs)
+{
+ unsigned count_w = countGlyphs(wfs);
+ unsigned count_wb = countGlyphs(wbfs);
+ if (count_w <= 256 ||
+ count_wb <= 256 ||
+ ((count_w / 4) * 3) > count_wb) {
+ TRACE(("...font server lied (count wide %u vs wide-bold %u)\n",
+ count_w, count_wb));
+ return False;
+ }
+ return True;
+}
+#endif /* OPT_WIDE_CHARS */
+
+#if OPT_BOX_CHARS
+static void
+setupPackedFonts(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+ Bool value = False;
+
+#if OPT_RENDERFONT
+#define MIXED(name) screen->name[fontnum].map.mixed
+ if (xw->misc.render_font == True) {
+ int fontnum = screen->menu_font_number;
+
+ screen->allow_packing = (Boolean) (MIXED(renderFontNorm)
+ || MIXED(renderFontBold)
+ || MIXED(renderFontItal)
+#if OPT_RENDERWIDE
+ || MIXED(renderWideNorm)
+ || MIXED(renderWideBold)
+ || MIXED(renderWideItal)
+#endif
+ );
+#undef MIXED
+ }
+#endif /* OPT_RENDERFONT */
+
+ value = screen->allow_packing;
+
+ SetItemSensitivity(fontMenuEntries[fontMenu_font_packedfont].widget, value);
+}
+#endif
+
+/*
+ * Returns the fields from start to stop in a dash- separated string. This
+ * function will modify the source, putting '\0's in the appropiate place and
+ * moving the beginning forward to after the '\0'
+ *
+ * This will NOT work for the last field (but we won't need it).
+ */
+static char *
+n_fields(char **source, int start, int stop)
+{
+ int i;
+ char *str, *str1;
+
+ /*
+ * find the start-1th dash
+ */
+ for (i = start - 1, str = *source; i; i--, str++)
+ if ((str = strchr(str, '-')) == 0)
+ return 0;
+
+ /*
+ * find the stopth dash
+ */
+ for (i = stop - start + 1, str1 = str; i; i--, str1++)
+ if ((str1 = strchr(str1, '-')) == 0)
+ return 0;
+
+ /*
+ * put a \0 at the end of the fields
+ */
+ *(str1 - 1) = '\0';
+
+ /*
+ * move source forward
+ */
+ *source = str1;
+
+ return str;
+}
+
+static Boolean
+check_fontname(const char *name)
+{
+ Boolean result = True;
+
+ if (IsEmpty(name)) {
+ TRACE(("fontname missing\n"));
+ result = False;
+ } else if (strlen(name) >= MAX_FONTNAME - 1) {
+ TRACE(("fontname too large: %s\n", name));
+ result = False;
+ }
+ return result;
+}
+
+/*
+ * Gets the font properties from a given font structure. We use the FONT name
+ * to find them out, since that seems easier.
+ *
+ * Returns a pointer to a static FontNameProperties structure
+ * or NULL on error.
+ */
+static FontNameProperties *
+get_font_name_props(Display * dpy, XFontStruct * fs, char *result)
+{
+ static FontNameProperties props;
+ static char *last_name;
+
+ XFontProp *fp;
+ int i;
+ Atom fontatom = XInternAtom(dpy, "FONT", False);
+ char *name = 0;
+ char *str;
+
+ /*
+ * first get the full font name
+ */
+ if (fontatom != 0) {
+ for (i = 0, fp = fs->properties; i < fs->n_properties; i++, fp++) {
+ if (fp->name == fontatom) {
+ name = XGetAtomName(dpy, fp->card32);
+ break;
+ }
+ }
+ }
+
+ if (name == 0)
+ return 0;
+
+ /*
+ * XGetAtomName allocates memory - don't leak
+ */
+ if (last_name != 0)
+ XFree(last_name);
+ last_name = name;
+
+ if (result != 0) {
+ if (!check_fontname(name))
+ return 0;
+ strcpy(result, name);
+ }
+
+ /*
+ * Now split it up into parts and put them in
+ * their places. Since we are using parts of
+ * the original string, we must not free the Atom Name
+ */
+
+ /* registry, foundry, family */
+ if ((props.beginning = n_fields(&name, 1, 3)) == 0)
+ return 0;
+
+ /* weight is the next */
+ if ((props.weight = n_fields(&name, 1, 1)) == 0)
+ return 0;
+
+ /* slant */
+ if ((props.slant = n_fields(&name, 1, 1)) == 0)
+ return 0;
+
+ /* width */
+ if ((props.wideness = n_fields(&name, 1, 1)) == 0)
+ return 0;
+
+ /* add style */
+ if ((props.add_style = n_fields(&name, 1, 1)) == 0)
+ return 0;
+
+ /* pixel size */
+ if ((str = n_fields(&name, 1, 1)) == 0)
+ return 0;
+ if ((props.pixel_size = atoi(str)) == 0)
+ return 0;
+
+ /* point size */
+ if ((props.point_size = n_fields(&name, 1, 1)) == 0)
+ return 0;
+
+ /* res_x */
+ if ((str = n_fields(&name, 1, 1)) == 0)
+ return 0;
+ if ((props.res_x = atoi(str)) == 0)
+ return 0;
+
+ /* res_y */
+ if ((str = n_fields(&name, 1, 1)) == 0)
+ return 0;
+ if ((props.res_y = atoi(str)) == 0)
+ return 0;
+
+ /* spacing */
+ if ((props.spacing = n_fields(&name, 1, 1)) == 0)
+ return 0;
+
+ /* average width */
+ if ((str = n_fields(&name, 1, 1)) == 0)
+ return 0;
+ if ((props.average_width = atoi(str)) == 0)
+ return 0;
+
+ /* the rest: charset registry and charset encoding */
+ props.end = name;
+
+ return &props;
+}
+
+#define ALLOCHUNK(n) ((n | 127) + 1)
+
+static void
+alloca_fontname(char **result, size_t next)
+{
+ size_t last = (*result != 0) ? strlen(*result) : 0;
+ size_t have = (*result != 0) ? ALLOCHUNK(last) : 0;
+ size_t want = last + next + 2;
+
+ if (want >= have) {
+ want = ALLOCHUNK(want);
+ if (last != 0) {
+ *result = TypeRealloc(char, want, *result);
+ } else {
+ if ((*result = TypeMallocN(char, want)) != 0)
+ **result = '\0';
+ }
+ }
+}
+
+static void
+append_fontname_str(char **result, const char *value)
+{
+ if (value == 0)
+ value = "*";
+ alloca_fontname(result, strlen(value));
+ if (*result != 0) {
+ if (**result != '\0')
+ strcat(*result, "-");
+ strcat(*result, value);
+ }
+}
+
+static void
+append_fontname_num(char **result, int value)
+{
+ if (value < 0) {
+ append_fontname_str(result, "*");
+ } else {
+ char temp[100];
+ sprintf(temp, "%d", value);
+ append_fontname_str(result, temp);
+ }
+}
+
+/*
+ * Take the given font props and try to make a well formed font name specifying
+ * the same base font and size and everything, but with different weight/width
+ * according to the parameters. The return value is allocated, should be freed
+ * by the caller.
+ */
+static char *
+derive_font_name(FontNameProperties * props,
+ const char *use_weight,
+ int use_average_width,
+ const char *use_encoding)
+{
+ char *result = 0;
+
+ append_fontname_str(&result, props->beginning);
+ append_fontname_str(&result, use_weight);
+ append_fontname_str(&result, props->slant);
+ append_fontname_str(&result, 0);
+ append_fontname_str(&result, 0);
+ append_fontname_num(&result, props->pixel_size);
+ append_fontname_str(&result, props->point_size);
+ append_fontname_num(&result, props->res_x);
+ append_fontname_num(&result, props->res_y);
+ append_fontname_str(&result, props->spacing);
+ append_fontname_num(&result, use_average_width);
+ append_fontname_str(&result, use_encoding);
+
+ return result;
+}
+
+static char *
+bold_font_name(FontNameProperties * props, int use_average_width)
+{
+ return derive_font_name(props, "bold", use_average_width, props->end);
+}
+
+#if OPT_WIDE_CHARS
+#define derive_wide_font(props, weight) \
+ derive_font_name(props, weight, props->average_width * 2, "ISO10646-1")
+
+static char *
+wide_font_name(FontNameProperties * props)
+{
+ return derive_wide_font(props, "medium");
+}
+
+static char *
+widebold_font_name(FontNameProperties * props)
+{
+ return derive_wide_font(props, "bold");
+}
+#endif /* OPT_WIDE_CHARS */
+
+#if OPT_DEC_CHRSET
+/*
+ * Take the given font props and try to make a well formed font name specifying
+ * the same base font but changed depending on the given attributes and chrset.
+ *
+ * For double width fonts, we just double the X-resolution, for double height
+ * fonts we double the pixel-size and Y-resolution
+ */
+char *
+xtermSpecialFont(TScreen * screen, unsigned atts, unsigned chrset)
+{
+#if OPT_TRACE
+ static char old_spacing[80];
+ static FontNameProperties old_props;
+#endif
+ FontNameProperties *props;
+ char *result = 0;
+ const char *weight;
+ int pixel_size;
+ int res_x;
+ int res_y;
+
+ props = get_font_name_props(screen->display, screen->fnts[fNorm].fs, 0);
+ if (props == 0)
+ return result;
+
+ pixel_size = props->pixel_size;
+ res_x = props->res_x;
+ res_y = props->res_y;
+ if (atts & BOLD)
+ weight = "bold";
+ else
+ weight = props->weight;
+
+ if (CSET_DOUBLE(chrset))
+ res_x *= 2;
+
+ if (chrset == CSET_DHL_TOP
+ || chrset == CSET_DHL_BOT) {
+ res_y *= 2;
+ pixel_size *= 2;
+ }
+#if OPT_TRACE
+ if (old_props.res_x != res_x
+ || old_props.res_x != res_y
+ || old_props.pixel_size != pixel_size
+ || strcmp(old_props.spacing, props->spacing)) {
+ TRACE(("xtermSpecialFont(atts = %#x, chrset = %#x)\n", atts, chrset));
+ TRACE(("res_x = %d\n", res_x));
+ TRACE(("res_y = %d\n", res_y));
+ TRACE(("point_size = %s\n", props->point_size));
+ TRACE(("pixel_size = %d\n", pixel_size));
+ TRACE(("spacing = %s\n", props->spacing));
+ old_props.res_x = res_x;
+ old_props.res_x = res_y;
+ old_props.pixel_size = pixel_size;
+ old_props.spacing = strcpy(old_spacing, props->spacing);
+ }
+#endif
+
+ append_fontname_str(&result, props->beginning);
+ append_fontname_str(&result, weight);
+ append_fontname_str(&result, props->slant);
+ append_fontname_str(&result, props->wideness);
+ append_fontname_str(&result, props->add_style);
+ append_fontname_num(&result, pixel_size);
+ append_fontname_str(&result, props->point_size);
+ append_fontname_num(&result, (atts & NORESOLUTION) ? -1 : res_x);
+ append_fontname_num(&result, (atts & NORESOLUTION) ? -1 : res_y);
+ append_fontname_str(&result, props->spacing);
+ append_fontname_str(&result, 0);
+ append_fontname_str(&result, props->end);
+
+ return result;
+}
+#endif /* OPT_DEC_CHRSET */
+
+/*
+ * Case-independent comparison for font-names, including wildcards.
+ * XLFD allows '?' as a wildcard, but we do not handle that (no one seems
+ * to use it).
+ */
+static Bool
+same_font_name(const char *pattern, const char *match)
+{
+ Bool result = False;
+
+ if (pattern && match) {
+ while (*pattern && *match) {
+ if (*pattern == *match) {
+ pattern++;
+ match++;
+ } else if (*pattern == '*' || *match == '*') {
+ if (same_font_name(pattern + 1, match)) {
+ return True;
+ } else if (same_font_name(pattern, match + 1)) {
+ return True;
+ } else {
+ return False;
+ }
+ } else {
+ int p = x_toupper(*pattern++);
+ int m = x_toupper(*match++);
+ if (p != m)
+ return False;
+ }
+ }
+ result = (*pattern == *match); /* both should be NUL */
+ }
+ return result;
+}
+
+/*
+ * Double-check the fontname that we asked for versus what the font server
+ * actually gave us. The larger fixed fonts do not always have a matching bold
+ * font, and the font server may try to scale another font or otherwise
+ * substitute a mismatched font.
+ *
+ * If we cannot get what we requested, we will fallback to the original
+ * behavior, which simulates bold by overstriking each character at one pixel
+ * offset.
+ */
+static int
+got_bold_font(Display * dpy, XFontStruct * fs, String requested)
+{
+ char actual[MAX_FONTNAME];
+ int got;
+
+ if (get_font_name_props(dpy, fs, actual) == 0)
+ got = 0;
+ else
+ got = same_font_name(requested, actual);
+ return got;
+}
+
+/*
+ * If the font server tries to adjust another font, it may not adjust it
+ * properly. Check that the bounding boxes are compatible. Otherwise we'll
+ * leave trash on the display when we mix normal and bold fonts.
+ */
+static int
+same_font_size(XtermWidget xw, XFontStruct * nfs, XFontStruct * bfs)
+{
+ TScreen *screen = TScreenOf(xw);
+ TRACE(("same_font_size height %d/%d, min %d/%d max %d/%d\n",
+ nfs->ascent + nfs->descent,
+ bfs->ascent + bfs->descent,
+ nfs->min_bounds.width, bfs->min_bounds.width,
+ nfs->max_bounds.width, bfs->max_bounds.width));
+ return screen->free_bold_box
+ || ((nfs->ascent + nfs->descent) == (bfs->ascent + bfs->descent)
+ && (nfs->min_bounds.width == bfs->min_bounds.width
+ || nfs->min_bounds.width == bfs->min_bounds.width + 1)
+ && (nfs->max_bounds.width == bfs->max_bounds.width
+ || nfs->max_bounds.width == bfs->max_bounds.width + 1));
+}
+
+/*
+ * Check if the font looks like it has fixed width
+ */
+static int
+is_fixed_font(XFontStruct * fs)
+{
+ if (fs)
+ return (fs->min_bounds.width == fs->max_bounds.width);
+ return 1;
+}
+
+/*
+ * Check if the font looks like a double width font (i.e. contains
+ * characters of width X and 2X
+ */
+#if OPT_WIDE_CHARS
+static int
+is_double_width_font(XFontStruct * fs)
+{
+ return ((2 * fs->min_bounds.width) == fs->max_bounds.width);
+}
+#else
+#define is_double_width_font(fs) 0
+#endif
+
+#if OPT_WIDE_CHARS && OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
+#define HALF_WIDTH_TEST_STRING "1234567890"
+
+/* '1234567890' in Chinese characters in UTF-8 */
+#define FULL_WIDTH_TEST_STRING "\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89" \
+ "\xe5\x9b\x9b\xe4\xba\x94" \
+ "\xef\xa7\x91\xe4\xb8\x83\xe5\x85\xab" \
+ "\xe4\xb9\x9d\xef\xa6\xb2"
+
+/* '1234567890' in Korean script in UTF-8 */
+#define FULL_WIDTH_TEST_STRING2 "\xec\x9d\xbc\xec\x9d\xb4\xec\x82\xbc" \
+ "\xec\x82\xac\xec\x98\xa4" \
+ "\xec\x9c\xa1\xec\xb9\xa0\xed\x8c\x94" \
+ "\xea\xb5\xac\xec\x98\x81"
+
+#define HALF_WIDTH_CHAR1 0x0031 /* '1' */
+#define HALF_WIDTH_CHAR2 0x0057 /* 'W' */
+#define FULL_WIDTH_CHAR1 0x4E00 /* CJK Ideograph 'number one' */
+#define FULL_WIDTH_CHAR2 0xAC00 /* Korean script syllable 'Ka' */
+
+static Bool
+is_double_width_font_xft(Display * dpy, XftFont * font)
+{
+ XGlyphInfo gi1, gi2;
+ FcChar32 c1 = HALF_WIDTH_CHAR1, c2 = HALF_WIDTH_CHAR2;
+ String fwstr = FULL_WIDTH_TEST_STRING;
+ String hwstr = HALF_WIDTH_TEST_STRING;
+
+ /* Some Korean fonts don't have Chinese characters at all. */
+ if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR1)) {
+ if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR2))
+ return False; /* Not a CJK font */
+ else /* a Korean font without CJK Ideographs */
+ fwstr = FULL_WIDTH_TEST_STRING2;
+ }
+
+ XftTextExtents32(dpy, font, &c1, 1, &gi1);
+ XftTextExtents32(dpy, font, &c2, 1, &gi2);
+ if (gi1.xOff != gi2.xOff) /* Not a fixed-width font */
+ return False;
+
+ XftTextExtentsUtf8(dpy,
+ font,
+ (_Xconst FcChar8 *) hwstr,
+ (int) strlen(hwstr),
+ &gi1);
+ XftTextExtentsUtf8(dpy,
+ font,
+ (_Xconst FcChar8 *) fwstr,
+ (int) strlen(fwstr),
+ &gi2);
+
+ /*
+ * fontconfig and Xft prior to 2.2(?) set the width of half-width
+ * characters identical to that of full-width character in CJK double-width
+ * (bi-width / monospace) font even though the former is half as wide as
+ * the latter. This was fixed sometime before the release of fontconfig
+ * 2.2 in early 2003. See
+ * http://bugzilla.mozilla.org/show_bug.cgi?id=196312
+ * In the meantime, we have to check both possibilities.
+ */
+ return ((2 * gi1.xOff == gi2.xOff) || (gi1.xOff == gi2.xOff));
+}
+#else
+#define is_double_width_font_xft(dpy, xftfont) 0
+#endif
+
+#define EmptyFont(fs) (fs != 0 \
+ && ((fs)->ascent + (fs)->descent == 0 \
+ || (fs)->max_bounds.width == 0))
+
+#define FontSize(fs) (((fs)->ascent + (fs)->descent) \
+ * (fs)->max_bounds.width)
+
+const VTFontNames *
+xtermFontName(const char *normal)
+{
+ static VTFontNames data;
+ if (data.f_n)
+ free((void *) data.f_n);
+ memset(&data, 0, sizeof(data));
+ data.f_n = x_strdup(normal);
+ return &data;
+}
+
+static void
+cache_menu_font_name(TScreen * screen, int fontnum, int which, const char *name)
+{
+ if (name != 0) {
+ char *last = (char *) screen->menu_font_names[fontnum][which];
+ if (last != 0) {
+ if (strcmp(last, name)) {
+ free(last);
+ TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
+ screen->menu_font_names[fontnum][which] = x_strdup(name);
+ }
+ } else {
+ TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
+ screen->menu_font_names[fontnum][which] = x_strdup(name);
+ }
+ }
+}
+
+/*
+ * Open the given font and verify that it is non-empty. Return a null on
+ * failure.
+ */
+Bool
+xtermOpenFont(XtermWidget xw,
+ const char *name,
+ XTermFonts * result,
+ fontWarningTypes warn,
+ Bool force)
+{
+ Bool code = False;
+ TScreen *screen = TScreenOf(xw);
+
+ if (!IsEmpty(name)) {
+ if ((result->fs = XLoadQueryFont(screen->display, name)) != 0) {
+ code = True;
+ if (EmptyFont(result->fs)) {
+ (void) xtermCloseFont(xw, result);
+ code = False;
+ } else {
+ result->fn = x_strdup(name);
+ }
+ } else if (XmuCompareISOLatin1(name, DEFFONT) != 0) {
+ if (warn <= xw->misc.fontWarnings
+#if OPT_RENDERFONT
+ && !UsingRenderFont(xw)
+#endif
+ ) {
+ TRACE(("OOPS: cannot load font %s\n", name));
+ xtermWarning("cannot load font '%s'\n", name);
+#if OPT_RENDERFONT
+ /*
+ * Do a sanity check in case someone's mixed up xterm with
+ * one of those programs that read their resource data from
+ * xterm's namespace.
+ */
+ if (strchr(name, ':') != 0 || strchr(name, '=') != 0) {
+ xtermWarning("Use the \"-fa\" option for the Xft fonts\n");
+ }
+#endif
+ } else {
+ TRACE(("xtermOpenFont: cannot load font '%s'\n", name));
+ }
+ if (force) {
+ code = xtermOpenFont(xw, DEFFONT, result, fwAlways, True);
+ }
+ }
+ }
+ return code;
+}
+
+/*
+ * Close the font and free the font info.
+ */
+XTermFonts *
+xtermCloseFont(XtermWidget xw, XTermFonts * fnt)
+{
+ if (fnt != 0 && fnt->fs != 0) {
+ TScreen *screen = TScreenOf(xw);
+
+ clrCgsFonts(xw, WhichVWin(screen), fnt);
+ XFreeFont(screen->display, fnt->fs);
+ xtermFreeFontInfo(fnt);
+ }
+ return 0;
+}
+
+/*
+ * Close the listed fonts, noting that some may use copies of the pointer.
+ */
+void
+xtermCloseFonts(XtermWidget xw, XTermFonts * fnts)
+{
+ int j, k;
+
+ for (j = 0; j < fMAX; ++j) {
+ /*
+ * Need to save the pointer since xtermCloseFont zeroes it
+ */
+ XFontStruct *thisFont = fnts[j].fs;
+ if (thisFont != 0) {
+ xtermCloseFont(xw, &fnts[j]);
+ for (k = j + 1; k < fMAX; ++k) {
+ if (thisFont == fnts[k].fs)
+ xtermFreeFontInfo(&fnts[k]);
+ }
+ }
+ }
+}
+
+/*
+ * Make a copy of the source, assuming the XFontStruct's to be unique, but
+ * ensuring that the names are reallocated to simplify freeing.
+ */
+void
+xtermCopyFontInfo(XTermFonts * target, XTermFonts * source)
+{
+ xtermFreeFontInfo(target);
+ target->chrset = source->chrset;
+ target->flags = source->flags;
+ target->fn = x_strdup(source->fn);
+ target->fs = source->fs;
+}
+
+void
+xtermFreeFontInfo(XTermFonts * target)
+{
+ target->chrset = 0;
+ target->flags = 0;
+ if (target->fn != 0) {
+ free(target->fn);
+ target->fn = 0;
+ }
+ target->fs = 0;
+}
+
+int
+xtermLoadFont(XtermWidget xw,
+ const VTFontNames * fonts,
+ Bool doresize,
+ int fontnum)
+{
+ TScreen *screen = TScreenOf(xw);
+ VTwin *win = WhichVWin(screen);
+
+ VTFontNames myfonts;
+ FontNameProperties *fp;
+ XTermFonts fnts[fMAX];
+ Pixel new_normal;
+ Pixel new_revers;
+ char *tmpname = NULL;
+ char normal[MAX_FONTNAME];
+ Boolean proportional = False;
+ fontWarningTypes warn[fMAX];
+ int j;
+
+ memset(&myfonts, 0, sizeof(myfonts));
+ memset(fnts, 0, sizeof(fnts));
+
+ if (fonts != 0)
+ myfonts = *fonts;
+ if (!check_fontname(myfonts.f_n))
+ return 0;
+
+ /*
+ * Check the font names against the resource values, to see which were
+ * derived in a previous call. If so, we'll only warn about those if
+ * the warning level is set to "always".
+ */
+ for (j = 0; j < fMAX; ++j) {
+ warn[j] = fwAlways;
+ }
+#define CmpResource(field, index) \
+ if (same_font_name(screen->menu_font_names[fontnum][index], myfonts.field)) \
+ warn[index] = fwResource
+
+ CmpResource(f_n, fNorm);
+ if (fontnum == fontMenu_default) {
+ CmpResource(f_b, fBold);
+#if OPT_WIDE_CHARS
+ CmpResource(f_b, fWide);
+ CmpResource(f_b, fWBold);
+#endif
+ }
+
+ if (fontnum == fontMenu_fontescape
+ && myfonts.f_n != screen->MenuFontName(fontnum)) {
+ if ((tmpname = x_strdup(myfonts.f_n)) == 0)
+ return 0;
+ }
+
+ TRACE(("Begin Cgs - xtermLoadFont(%s)\n", myfonts.f_n));
+ releaseWindowGCs(xw, win);
+
+#define DbgResource(name, field, index) \
+ TRACE(("xtermLoadFont #%d "name" %s%s\n", \
+ fontnum, \
+ (warn[index] == fwResource) ? "*" : " ", \
+ NonNull(myfonts.field)))
+ DbgResource("normal", f_n, fNorm);
+ DbgResource("bold ", f_b, fBold);
+#if OPT_WIDE_CHARS
+ DbgResource("wide ", f_w, fWide);
+ DbgResource("w/bold", f_wb, fWBold);
+#endif
+
+ /*
+ * If we are opening the default font, and it happens to be missing, force
+ * that to the compiled-in default font, e.g., "fixed". If we cannot open
+ * the font, disable it from the menu.
+ */
+ if (!xtermOpenFont(xw,
+ myfonts.f_n,
+ &fnts[fNorm],
+ warn[fNorm],
+ (fontnum == fontMenu_default))) {
+ SetItemSensitivity(fontMenuEntries[fontnum].widget, False);
+ goto bad;
+ }
+
+ strcpy(normal, myfonts.f_n);
+ if (!check_fontname(myfonts.f_b)) {
+ warn[fBold] = fwAlways;
+ fp = get_font_name_props(screen->display, fnts[fNorm].fs, normal);
+ if (fp != 0) {
+ myfonts.f_b = bold_font_name(fp, fp->average_width);
+ if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False)) {
+ myfonts.f_b = bold_font_name(fp, -1);
+ xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False);
+ }
+ TRACE(("...derived bold '%s'\n", NonNull(myfonts.f_b)));
+ }
+ if (fp == 0 || fnts[fBold].fs == 0) {
+ xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
+ TRACE(("...cannot load a matching bold font\n"));
+ } else if (same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
+ && got_bold_font(screen->display, fnts[fBold].fs, myfonts.f_b)) {
+ TRACE(("...got a matching bold font\n"));
+ cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
+ } else {
+ xtermCloseFont(xw, &fnts[fBold]);
+ fnts[fBold] = fnts[fNorm];
+ TRACE(("...did not get a matching bold font\n"));
+ }
+ } else if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], warn[fBold], False)) {
+ xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
+ warn[fBold] = fwAlways;
+ TRACE(("...cannot load bold font '%s'\n", NonNull(myfonts.f_b)));
+ } else {
+ cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
+ }
+
+ /*
+ * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH
+ * of normal fonts XLFD, and asking for it. This plucks out 18x18ja
+ * and 12x13ja as the corresponding fonts for 9x18 and 6x13.
+ */
+ if_OPT_WIDE_CHARS(screen, {
+ Boolean derived;
+ char bold[MAX_FONTNAME];
+
+ if (check_fontname(myfonts.f_w)) {
+ cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
+ } else if (screen->utf8_fonts && !is_double_width_font(fnts[fNorm].fs)) {
+ fp = get_font_name_props(screen->display, fnts[fNorm].fs, normal);
+ if (fp != 0) {
+ myfonts.f_w = wide_font_name(fp);
+ warn[fWide] = fwAlways;
+ TRACE(("...derived wide %s\n", NonNull(myfonts.f_w)));
+ cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
+ }
+ }
+
+ if (check_fontname(myfonts.f_w)) {
+ (void) xtermOpenFont(xw, myfonts.f_w, &fnts[fWide], warn[fWide], False);
+ } else {
+ xtermCopyFontInfo(&fnts[fWide], &fnts[fNorm]);
+ warn[fWide] = fwAlways;
+ }
+
+ derived = False;
+ if (!check_fontname(myfonts.f_wb)) {
+ fp = get_font_name_props(screen->display, fnts[fBold].fs, bold);
+ if (fp != 0) {
+ myfonts.f_wb = widebold_font_name(fp);
+ warn[fWBold] = fwAlways;
+ derived = True;
+ }
+ }
+
+ if (check_fontname(myfonts.f_wb)) {
+
+ xtermOpenFont(xw,
+ myfonts.f_wb,
+ &fnts[fWBold],
+ (screen->utf8_fonts
+ ? warn[fWBold]
+ : (fontWarningTypes) (xw->misc.fontWarnings + 1)),
+ False);
+
+ if (derived
+ && !compatibleWideCounts(fnts[fWide].fs, fnts[fWBold].fs)) {
+ xtermCloseFont(xw, &fnts[fWBold]);
+ }
+ if (fnts[fWBold].fs == 0) {
+ if (IsEmpty(myfonts.f_w)) {
+ myfonts.f_wb = myfonts.f_b;
+ warn[fWBold] = fwAlways;
+ xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
+ TRACE(("...cannot load wide-bold, use bold %s\n",
+ NonNull(myfonts.f_b)));
+ } else {
+ myfonts.f_wb = myfonts.f_w;
+ warn[fWBold] = fwAlways;
+ xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
+ TRACE(("...cannot load wide-bold, use wide %s\n",
+ NonNull(myfonts.f_w)));
+ }
+ } else {
+ TRACE(("...%s wide/bold %s\n",
+ derived ? "derived" : "given",
+ NonNull(myfonts.f_wb)));
+ cache_menu_font_name(screen, fontnum, fWBold, myfonts.f_wb);
+ }
+ } else if (is_double_width_font(fnts[fBold].fs)) {
+ xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
+ warn[fWBold] = fwAlways;
+ TRACE(("...bold font is double-width, use it %s\n", NonNull(myfonts.f_b)));
+ } else {
+ xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
+ warn[fWBold] = fwAlways;
+ TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(myfonts.f_w)));
+ }
+
+ if (EmptyFont(fnts[fWBold].fs))
+ goto bad; /* can't use a 0-sized font */
+ });
+
+ /*
+ * Most of the time this call to load the font will succeed, even if
+ * there is no wide font : the X server doubles the width of the
+ * normal font, or similar.
+ *
+ * But if it did fail for some reason, then nevermind.
+ */
+ if (EmptyFont(fnts[fBold].fs))
+ goto bad; /* can't use a 0-sized font */
+
+ if (!same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
+ && (is_fixed_font(fnts[fNorm].fs) && is_fixed_font(fnts[fBold].fs))) {
+ TRACE(("...ignoring mismatched normal/bold fonts\n"));
+ xtermCloseFont(xw, &fnts[fBold]);
+ xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
+ }
+
+ if_OPT_WIDE_CHARS(screen, {
+ if (fnts[fWide].fs != 0
+ && fnts[fWBold].fs != 0
+ && !same_font_size(xw, fnts[fWide].fs, fnts[fWBold].fs)
+ && (is_fixed_font(fnts[fWide].fs) && is_fixed_font(fnts[fWBold].fs))) {
+ TRACE(("...ignoring mismatched normal/bold wide fonts\n"));
+ xtermCloseFont(xw, &fnts[fWBold]);
+ xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
+ }
+ });
+
+ /*
+ * Normal/bold fonts should be the same width. Also, the min/max
+ * values should be the same.
+ */
+ if (!is_fixed_font(fnts[fNorm].fs)
+ || !is_fixed_font(fnts[fBold].fs)
+ || fnts[fNorm].fs->max_bounds.width != fnts[fBold].fs->max_bounds.width) {
+ TRACE(("Proportional font! normal %d/%d, bold %d/%d\n",
+ fnts[fNorm].fs->min_bounds.width,
+ fnts[fNorm].fs->max_bounds.width,
+ fnts[fBold].fs->min_bounds.width,
+ fnts[fBold].fs->max_bounds.width));
+ proportional = True;
+ }
+
+ if_OPT_WIDE_CHARS(screen, {
+ if (fnts[fWide].fs != 0
+ && fnts[fWBold].fs != 0
+ && (!is_fixed_font(fnts[fWide].fs)
+ || !is_fixed_font(fnts[fWBold].fs)
+ || fnts[fWide].fs->max_bounds.width != fnts[fWBold].fs->max_bounds.width)) {
+ TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n",
+ fnts[fWide].fs->min_bounds.width,
+ fnts[fWide].fs->max_bounds.width,
+ fnts[fWBold].fs->min_bounds.width,
+ fnts[fWBold].fs->max_bounds.width));
+ proportional = True;
+ }
+ });
+
+ /* TODO : enforce that the width of the wide font is 2* the width
+ of the narrow font */
+
+ /*
+ * If we're switching fonts, free the old ones. Otherwise we'll leak
+ * the memory that is associated with the old fonts. The
+ * XLoadQueryFont call allocates a new XFontStruct.
+ */
+ xtermCloseFonts(xw, screen->fnts);
+
+ xtermCopyFontInfo(&(screen->fnts[fNorm]), &fnts[fNorm]);
+ xtermCopyFontInfo(&(screen->fnts[fBold]), &fnts[fBold]);
+#if OPT_WIDE_CHARS
+ xtermCopyFontInfo(&(screen->fnts[fWide]), &fnts[fWide]);
+ if (fnts[fWBold].fs == NULL)
+ xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
+ xtermCopyFontInfo(&(screen->fnts[fWBold]), &fnts[fWBold]);
+#endif
+
+ new_normal = getXtermForeground(xw, xw->flags, xw->cur_foreground);
+ new_revers = getXtermBackground(xw, xw->flags, xw->cur_background);
+
+ setCgsFore(xw, win, gcNorm, new_normal);
+ setCgsBack(xw, win, gcNorm, new_revers);
+ setCgsFont(xw, win, gcNorm, &(screen->fnts[fNorm]));
+
+ copyCgs(xw, win, gcBold, gcNorm);
+ setCgsFont(xw, win, gcBold, &(screen->fnts[fBold]));
+
+ setCgsFore(xw, win, gcNormReverse, new_revers);
+ setCgsBack(xw, win, gcNormReverse, new_normal);
+ setCgsFont(xw, win, gcNormReverse, &(screen->fnts[fNorm]));
+
+ copyCgs(xw, win, gcBoldReverse, gcNormReverse);
+ setCgsFont(xw, win, gcBoldReverse, &(screen->fnts[fBold]));
+
+ if_OPT_WIDE_CHARS(screen, {
+ if (screen->fnts[fWide].fs != 0
+ && screen->fnts[fWBold].fs != 0) {
+ setCgsFore(xw, win, gcWide, new_normal);
+ setCgsBack(xw, win, gcWide, new_revers);
+ setCgsFont(xw, win, gcWide, &(screen->fnts[fWide]));
+
+ copyCgs(xw, win, gcWBold, gcWide);
+ setCgsFont(xw, win, gcWBold, &(screen->fnts[fWBold]));
+
+ setCgsFore(xw, win, gcWideReverse, new_revers);
+ setCgsBack(xw, win, gcWideReverse, new_normal);
+ setCgsFont(xw, win, gcWideReverse, &(screen->fnts[fWide]));
+
+ copyCgs(xw, win, gcWBoldReverse, gcWideReverse);
+ setCgsFont(xw, win, gcWBoldReverse, &(screen->fnts[fWBold]));
+ }
+ });
+
+#if OPT_BOX_CHARS
+ screen->allow_packing = proportional;
+ setupPackedFonts(xw);
+#endif
+ screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed));
+ screen->fnt_boxes = True;
+
+#if OPT_BOX_CHARS
+ /*
+ * Xterm uses character positions 1-31 of a font for the line-drawing
+ * characters. Check that they are all present. The null character
+ * (0) is special, and is not used.
+ */
+#if OPT_RENDERFONT
+ if (UsingRenderFont(xw)) {
+ /*
+ * FIXME: we shouldn't even be here if we're using Xft.
+ */
+ screen->fnt_boxes = False;
+ TRACE(("assume Xft missing line-drawing chars\n"));
+ } else
+#endif
+ {
+ unsigned ch;
+
+ for (ch = 1; ch < 32; ch++) {
+ unsigned n = ch;
+#if OPT_WIDE_CHARS
+ if (screen->utf8_mode || screen->unicode_font) {
+ n = dec2ucs(ch);
+ if (n == UCS_REPL)
+ continue;
+ }
+#endif
+ if (IsXtermMissingChar(screen, n, &fnts[fNorm])) {
+ TRACE(("missing normal char #%d\n", n));
+ screen->fnt_boxes = False;
+ break;
+ }
+ if (IsXtermMissingChar(screen, n, &fnts[fBold])) {
+ TRACE(("missing bold char #%d\n", n));
+ screen->fnt_boxes = False;
+ break;
+ }
+ }
+ }
+ TRACE(("Will %suse internal line-drawing characters\n",
+ screen->fnt_boxes ? "not " : ""));
+#endif
+
+ if (screen->always_bold_mode) {
+ screen->enbolden = screen->bold_mode;
+ } else {
+ screen->enbolden = screen->bold_mode
+ && ((fnts[fNorm].fs == fnts[fBold].fs)
+ || same_font_name(normal, myfonts.f_b));
+ }
+ TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n",
+ screen->enbolden ? "" : "not "));
+
+ set_menu_font(False);
+ screen->menu_font_number = fontnum;
+ set_menu_font(True);
+ if (tmpname) { /* if setting escape or sel */
+ if (screen->MenuFontName(fontnum))
+ free((void *) screen->MenuFontName(fontnum));
+ screen->MenuFontName(fontnum) = tmpname;
+ if (fontnum == fontMenu_fontescape) {
+ SetItemSensitivity(fontMenuEntries[fontMenu_fontescape].widget,
+ True);
+ }
+#if OPT_SHIFT_FONTS
+ screen->menu_font_sizes[fontnum] = FontSize(fnts[fNorm].fs);
+#endif
+ }
+ set_cursor_gcs(xw);
+ xtermUpdateFontInfo(xw, doresize);
+ TRACE(("Success Cgs - xtermLoadFont\n"));
+ return 1;
+
+ bad:
+ if (tmpname)
+ free(tmpname);
+
+#if OPT_RENDERFONT
+ if (x_strcasecmp(myfonts.f_n, DEFFONT)) {
+ int code;
+
+ myfonts.f_n = DEFFONT;
+ TRACE(("...recovering for TrueType fonts\n"));
+ code = xtermLoadFont(xw, &myfonts, doresize, fontnum);
+ if (code) {
+ SetItemSensitivity(fontMenuEntries[fontnum].widget,
+ UsingRenderFont(xw));
+ TRACE(("...recovered size %dx%d\n",
+ FontHeight(screen),
+ FontWidth(screen)));
+ }
+ return code;
+ }
+#endif
+
+ releaseWindowGCs(xw, win);
+
+ xtermCloseFonts(xw, fnts);
+ TRACE(("Fail Cgs - xtermLoadFont\n"));
+ return 0;
+}
+
+#if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
+/*
+ * Collect font-names that we can modify with the load-vt-fonts() action.
+ */
+#define MERGE_SUBFONT(src,dst,name) \
+ if (IsEmpty(dst.name)) { \
+ TRACE(("MERGE_SUBFONT " #dst "." #name " merge %s\n", NonNull(src.name))); \
+ dst.name = src.name; \
+ } else { \
+ TRACE(("MERGE_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
+ }
+
+#define INFER_SUBFONT(src,dst,name) \
+ if (IsEmpty(dst.name)) { \
+ TRACE(("INFER_SUBFONT " #dst "." #name " will infer\n")); \
+ dst.name = x_strdup(""); \
+ } else { \
+ TRACE(("INFER_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
+ }
+
+#define COPY_MENU_FONTS(src,dst) \
+ TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \
+ for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
+ for (m = 0; m < fMAX; ++m) { \
+ dst.menu_font_names[n][m] = x_strdup(src.menu_font_names[n][m]); \
+ } \
+ TRACE((".. " #dst ".menu_fonts_names[%d] = %s\n", n, dst.menu_font_names[n][fNorm])); \
+ }
+
+void
+xtermSaveVTFonts(XtermWidget xw)
+{
+ TScreen *screen = TScreenOf(xw);
+ Cardinal n, m;
+
+ if (!screen->savedVTFonts) {
+
+ screen->savedVTFonts = True;
+ TRACE(("xtermSaveVTFonts saving original\n"));
+ screen->cacheVTFonts.default_font = xw->misc.default_font;
+ COPY_MENU_FONTS(xw->screen, screen->cacheVTFonts);
+ }
+}
+
+#define SAME_STRING(x,y) ((x) == (y) || ((x) && (y) && !strcmp(x, y)))
+#define SAME_MEMBER(n) SAME_STRING(a->n, b->n)
+
+static Boolean
+sameSubResources(SubResourceRec * a, SubResourceRec * b)
+{
+ Boolean result = True;
+ int n;
+
+ if (!SAME_MEMBER(default_font.f_n)
+ || !SAME_MEMBER(default_font.f_b)
+#if OPT_WIDE_CHARS
+ || !SAME_MEMBER(default_font.f_w)
+ || !SAME_MEMBER(default_font.f_wb)
+#endif
+ ) {
+ TRACE(("sameSubResources: default_font differs\n"));
+ result = False;
+ } else {
+ for (n = 0; n < NMENUFONTS; ++n) {
+ if (!SAME_MEMBER(menu_font_names[n][fNorm])) {
+ TRACE(("sameSubResources: menu_font_names[%d] differs\n", n));
+ result = False;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Load the "VT" font names from the given subresource name/class. These
+ * correspond to the VT100 resources.
+ */
+static Bool
+xtermLoadVTFonts(XtermWidget xw, String myName, String myClass)
+{
+ SubResourceRec subresourceRec;
+ SubResourceRec referenceRec;
+
+ /*
+ * These are duplicates of the VT100 font resources, but with a special
+ * application/classname passed in to distinguish them.
+ */
+ static XtResource font_resources[] =
+ {
+ Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT),
+ Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT),
+#if OPT_WIDE_CHARS
+ Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT),
+ Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT),
+#endif
+ Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL),
+ Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL),
+ Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL),
+ Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL),
+ Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL),
+ Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL),
+ };
+ Cardinal n, m;
+ Bool status = True;
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("called xtermLoadVTFonts(name=%s, class=%s)\n",
+ NonNull(myName), NonNull(myClass)));
+
+ xtermSaveVTFonts(xw);
+
+ if (IsEmpty(myName)) {
+ TRACE(("xtermLoadVTFonts restoring original\n"));
+ xw->misc.default_font = screen->cacheVTFonts.default_font;
+ COPY_MENU_FONTS(screen->cacheVTFonts, xw->screen);
+ for (n = 0; n < XtNumber(screen->cacheVTFonts.menu_font_names); ++n) {
+ screen->MenuFontName(n) = screen->cacheVTFonts.MenuFontName(n);
+ }
+ } else {
+ TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass));
+
+ memset(&referenceRec, 0, sizeof(referenceRec));
+ memset(&subresourceRec, 0, sizeof(subresourceRec));
+ XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec,
+ myName, myClass,
+ font_resources,
+ (Cardinal) XtNumber(font_resources),
+ NULL, (Cardinal) 0);
+
+ /*
+ * XtGetSubresources returns no status, so we compare the returned
+ * data against a zero'd struct to see if any data is returned.
+ */
+ if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec))
+ && !sameSubResources(&(screen->cacheVTFonts), &subresourceRec)) {
+
+ screen->mergedVTFonts = True;
+
+ /*
+ * If a particular resource value was not found, use the original.
+ */
+ MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_n);
+ INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_b);
+#if OPT_WIDE_CHARS
+ INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_w);
+ INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_wb);
+#endif
+ for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n)
+ MERGE_SUBFONT(xw->screen, subresourceRec, MenuFontName(n));
+
+ /*
+ * Finally, copy the subresource data to the widget.
+ */
+ xw->misc.default_font = subresourceRec.default_font;
+ COPY_MENU_FONTS(subresourceRec, xw->screen);
+ screen->MenuFontName(fontMenu_default) = x_strdup(xw->misc.default_font.f_n);
+ screen->menu_font_names[0][fBold] = x_strdup(xw->misc.default_font.f_b);
+#if OPT_WIDE_CHARS
+ screen->menu_font_names[0][fWide] = x_strdup(xw->misc.default_font.f_w);
+ screen->menu_font_names[0][fWBold] = x_strdup(xw->misc.default_font.f_wb);
+#endif
+ } else {
+ TRACE(("...no resources found\n"));
+ status = False;
+ }
+ }
+ return status;
+}
+
+#if OPT_WIDE_CHARS
+static Bool
+isWideFont(XFontStruct * fp, const char *tag, Bool nullOk)
+{
+ Bool result = False;
+
+ (void) tag;
+ if (okFont(fp)) {
+ unsigned count = countGlyphs(fp);
+ TRACE(("isWideFont(%s) found %d cells\n", tag, count));
+ result = (count > 256) ? True : False;
+ } else {
+ result = nullOk;
+ }
+ return result;
+}
+
+/*
+ * If the current fonts are not wide, load the UTF8 fonts.
+ *
+ * Called during initialization (for wide-character mode), the fonts have not
+ * been setup, so we pass nullOk=True to isWideFont().
+ *
+ * Called after initialization, e.g., in response to the UTF-8 menu entry
+ * (starting from narrow character mode), it checks if the fonts are not wide.
+ */
+Bool
+xtermLoadWideFonts(XtermWidget xw, Bool nullOk)
+{
+ TScreen *screen = TScreenOf(xw);
+ Bool result;
+
+ if (EmptyFont(screen->fnts[fWide].fs)) {
+ result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
+ && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
+ } else {
+ result = (isWideFont(screen->fnts[fWide].fs, "wide", nullOk)
+ && isWideFont(screen->fnts[fWBold].fs, "wide-bold", nullOk));
+ if (result && !screen->utf8_latin1) {
+ result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
+ && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
+ }
+ }
+ if (!result) {
+ TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : ""));
+ result = xtermLoadVTFonts(xw, XtNutf8Fonts, XtCUtf8Fonts);
+ }
+ TRACE(("xtermLoadWideFonts:%d\n", result));
+ return result;
+}
+#endif /* OPT_WIDE_CHARS */
+
+/*
+ * Restore the default fonts, i.e., if we had switched to wide-fonts.
+ */
+Bool
+xtermLoadDefaultFonts(XtermWidget xw)
+{
+ Bool result;
+ result = xtermLoadVTFonts(xw, NULL, NULL);
+ TRACE(("xtermLoadDefaultFonts:%d\n", result));
+ return result;
+}
+#endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */
+
+#if OPT_LOAD_VTFONTS
+void
+HandleLoadVTFonts(Widget w,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *param_count GCC_UNUSED)
+{
+ static char empty[] = ""; /* appease strict compilers */
+
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ TScreen *screen = TScreenOf(xw);
+ char name_buf[80];
+ char class_buf[80];
+ String name = (String) ((*param_count > 0) ? params[0] : empty);
+ char *myName = (char *) MyStackAlloc(strlen(name), name_buf);
+ String convert = (String) ((*param_count > 1) ? params[1] : myName);
+ char *myClass = (char *) MyStackAlloc(strlen(convert), class_buf);
+ int n;
+
+ TRACE(("HandleLoadVTFonts(%d)\n", *param_count));
+ strcpy(myName, name);
+ strcpy(myClass, convert);
+ if (*param_count == 1)
+ myClass[0] = x_toupper(myClass[0]);
+
+ if (xtermLoadVTFonts(xw, myName, myClass)) {
+ /*
+ * When switching fonts, try to preserve the font-menu selection, since
+ * it is less surprising to do that (if the font-switching can be
+ * undone) than to switch to "Default".
+ */
+ int font_number = screen->menu_font_number;
+ if (font_number > fontMenu_lastBuiltin)
+ font_number = fontMenu_lastBuiltin;
+ for (n = 0; n < NMENUFONTS; ++n)
+ screen->menu_font_sizes[n] = 0;
+ SetVTFont(xw, font_number, True,
+ ((font_number == fontMenu_default)
+ ? &(xw->misc.default_font)
+ : NULL));
+ }
+
+ MyStackFree(myName, name_buf);
+ MyStackFree(myClass, class_buf);
+ }
+}
+#endif /* OPT_LOAD_VTFONTS */
+
+/*
+ * Set the limits for the box that outlines the cursor.
+ */
+void
+xtermSetCursorBox(TScreen * screen)
+{
+ static XPoint VTbox[NBOX];
+ XPoint *vp;
+ int fw = FontWidth(screen) - 1;
+ int fh = FontHeight(screen) - 1;
+ int hh = screen->cursor_underline ? 1 : fh;
+
+ vp = &VTbox[1];
+ (vp++)->x = (short) fw;
+ (vp++)->y = (short) hh;
+ (vp++)->x = (short) -fw;
+ vp->y = (short) -hh;
+
+ screen->box = VTbox;
+}
+
+#define CACHE_XFT(dst,src) if (src != 0) {\
+ checkXft(xw, &(dst[fontnum]), src);\
+ TRACE(("Xft metrics %s[%d] = %d (%d,%d)%s advance %d, actual %d%s\n",\
+ #dst,\
+ fontnum,\
+ src->height,\
+ src->ascent,\
+ src->descent,\
+ ((src->ascent + src->descent) > src->height ? "*" : ""),\
+ src->max_advance_width,\
+ dst[fontnum].map.min_width,\
+ dst[fontnum].map.mixed ? " mixed" : ""));\
+ }
+
+#if OPT_RENDERFONT
+
+#if OPT_TRACE > 1
+static FcChar32
+xtermXftFirstChar(XftFont * xft)
+{
+ FcChar32 map[FC_CHARSET_MAP_SIZE];
+ FcChar32 next;
+ FcChar32 first;
+ int i;
+
+ first = FcCharSetFirstPage(xft->charset, map, &next);
+ for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
+ if (map[i]) {
+ FcChar32 bits = map[i];
+ first += i * 32;
+ while (!(bits & 0x1)) {
+ bits >>= 1;
+ first++;
+ }
+ break;
+ }
+ return first;
+}
+
+static FcChar32
+xtermXftLastChar(XftFont * xft)
+{
+ FcChar32 this, last, next;
+ FcChar32 map[FC_CHARSET_MAP_SIZE];
+ int i;
+ last = FcCharSetFirstPage(xft->charset, map, &next);
+ while ((this = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE)
+ last = this;
+ last &= ~0xff;
+ for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
+ if (map[i]) {
+ FcChar32 bits = map[i];
+ last += i * 32 + 31;
+ while (!(bits & 0x80000000)) {
+ last--;
+ bits <<= 1;
+ }
+ break;
+ }
+ return (long) last;
+}
+
+static void
+dumpXft(XtermWidget xw, XTermXftFonts * data)
+{
+ XftFont *xft = data->font;
+ TScreen *screen = TScreenOf(xw);
+ VTwin *win = WhichVWin(screen);
+
+ FcChar32 c;
+ FcChar32 first = xtermXftFirstChar(xft);
+ FcChar32 last = xtermXftLastChar(xft);
+ unsigned count = 0;
+ unsigned outside = 0;
+
+ TRACE(("dumpXft {{\n"));
+ TRACE((" data range %#6x..%#6x\n", first, last));
+ for (c = first; c <= last; ++c) {
+ if (FcCharSetHasChar(xft->charset, c)) {
+ int width = my_wcwidth((int) c);
+ XGlyphInfo extents;
+
+ XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
+ TRACE(("%#6x %2d %.1f\n", c, width,
+ ((double) extents.width) / win->f_width));
+ if (extents.width > win->f_width)
+ ++outside;
+ ++count;
+ }
+ }
+ TRACE(("}} %u total, %u outside\n", count, outside));
+}
+#define DUMP_XFT(xw, data) dumpXft(xw, data)
+#else
+#define DUMP_XFT(xw, data) /* nothing */
+#endif
+
+static void
+checkXft(XtermWidget xw, XTermXftFonts * data, XftFont * xft)
+{
+ FcChar32 c;
+ Dimension width = 0;
+
+ data->font = xft;
+ data->map.min_width = 0;
+ data->map.max_width = (Dimension) xft->max_advance_width;
+
+ /*
+ * For each ASCII or ISO-8859-1 printable code, ask what its width is.
+ * Given the maximum width for those, we have a reasonable estimate of
+ * the single-column width.
+ *
+ * Ignore control characters - their extent information is misleading.
+ */
+ for (c = 32; c < 256; ++c) {
+ if (c >= 127 && c <= 159)
+ continue;
+ if (FcCharSetHasChar(xft->charset, c)) {
+ XGlyphInfo extents;
+
+ XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
+ if (width < extents.width && extents.width <= data->map.max_width) {
+ width = extents.width;
+ }
+ }
+ }
+ data->map.min_width = width;
+ data->map.mixed = (data->map.max_width >= (data->map.min_width + 1));
+}
+
+static XftFont *
+xtermOpenXft(XtermWidget xw, const char *name, XftPattern * pat, const char *tag)
+{
+ TScreen *screen = TScreenOf(xw);
+ Display *dpy = screen->display;
+ XftPattern *match;
+ XftResult status;
+ XftFont *result = 0;
+
+ if (pat != 0) {
+ match = XftFontMatch(dpy, DefaultScreen(dpy), pat, &status);
+ if (match != 0) {
+ result = XftFontOpenPattern(dpy, match);
+ if (result != 0) {
+ TRACE(("...matched %s font\n", tag));
+ } else {
+ TRACE(("...could did not open %s font\n", tag));
+ XftPatternDestroy(match);
+ if (xw->misc.fontWarnings >= fwAlways) {
+ TRACE(("OOPS cannot open %s font \"%s\"\n", tag, name));
+ xtermWarning("cannot open %s font \"%s\"\n", tag, name);
+ }
+ }
+ } else {
+ TRACE(("...did not match %s font\n", tag));
+ if (xw->misc.fontWarnings >= fwResource) {
+ TRACE(("OOPS: cannot match %s font \"%s\"\n", tag, name));
+ xtermWarning("cannot match %s font \"%s\"\n", tag, name);
+ }
+ }
+ }
+ return result;
+}
+#endif
+
+#if OPT_RENDERFONT
+#if OPT_SHIFT_FONTS
+/*
+ * Don't make a dependency on the math library for a single function.
+ * (Newton Raphson).
+ */
+static double
+dimSquareRoot(double value)
+{
+ double result = 0.0;
+ if (value > 0.0) {
+ int n;
+ double older = value;
+ for (n = 0; n < 10; ++n) {
+ double delta = (older * older - value) / (2.0 * older);
+ double newer = older - delta;
+ older = newer;
+ result = newer;
+ if (delta > -0.001 && delta < 0.001)
+ break;
+ }
+ }
+ return result;
+}
+#endif
+
+/*
+ * Given the Xft font metrics, determine the actual font size. This is used
+ * for each font to ensure that normal, bold and italic fonts follow the same
+ * rule.
+ */
+static void
+setRenderFontsize(TScreen * screen, VTwin * win, XftFont * font, const char *tag)
+{
+ if (font != 0) {
+ int width, height, ascent, descent;
+
+ (void) screen;
+
+ width = font->max_advance_width;
+ height = font->height;
+ ascent = font->ascent;
+ descent = font->descent;
+ if (height < ascent + descent) {
+ TRACE(("...increase height from %d\n", height));
+ height = ascent + descent;
+ }
+ if (is_double_width_font_xft(screen->display, font)) {
+ TRACE(("...reduced width from %d\n", width));
+ width >>= 1;
+ }
+ if (tag == 0) {
+ SetFontWidth(screen, win, width);
+ SetFontHeight(screen, win, height);
+ win->f_ascent = ascent;
+ win->f_descent = descent;
+ TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
+ width, height, ascent, descent));
+ } else if (win->f_width < width ||
+ win->f_height < height ||
+ win->f_ascent < ascent ||
+ win->f_descent < descent) {
+ TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
+ tag,
+ win->f_width, win->f_height, win->f_ascent, win->f_descent,
+ width, height, ascent, descent));
+
+ SetFontWidth(screen, win, width);
+ SetFontHeight(screen, win, height);
+ win->f_ascent = ascent;
+ win->f_descent = descent;
+ } else {
+ TRACE(("setRenderFontsize %s unchanged\n", tag));
+ }
+ }
+}
+#endif
+
+static void
+checkFontInfo(int value, const char *tag)
+{
+ if (value == 0) {
+ xtermWarning("Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag);
+ exit(1);
+ }
+}
+
+#if OPT_RENDERFONT
+void
+xtermCloseXft(TScreen * screen, XTermXftFonts * pub)
+{
+ if (pub->font != 0) {
+ XftFontClose(screen->display, pub->font);
+ pub->font = 0;
+ }
+}
+
+/*
+ * Get the faceName/faceDoublesize resource setting. Strip off "xft:", which
+ * is not recognized by XftParseName().
+ */
+String
+getFaceName(XtermWidget xw, Bool wideName GCC_UNUSED)
+{
+#if OPT_RENDERWIDE
+ String result = (wideName
+ ? xw->misc.face_wide_name
+ : xw->misc.face_name);
+#else
+ String result = xw->misc.face_name;
+#endif
+ if (!IsEmpty(result) && !strncmp(result, "xft:", (size_t) 4))
+ result += 4;
+ return x_nonempty(result);
+}
+
+/*
+ * If we change the faceName, we'll have to re-acquire all of the fonts that
+ * are derived from it.
+ */
+void
+setFaceName(XtermWidget xw, const char *value)
+{
+ TScreen *screen = TScreenOf(xw);
+ int n;
+ Boolean changed = (Boolean) ((xw->misc.face_name == 0)
+ || strcmp(xw->misc.face_name, value));
+
+ if (changed) {
+ xw->misc.face_name = x_strdup(value);
+ for (n = 0; n < NMENUFONTS; ++n) {
+ xw->misc.face_size[n] = -1.0;
+ xtermCloseXft(screen, &(screen->renderFontNorm[n]));
+ xtermCloseXft(screen, &(screen->renderFontBold[n]));
+ xtermCloseXft(screen, &(screen->renderFontBold[n]));
+#if OPT_RENDERWIDE
+ xtermCloseXft(screen, &(screen->renderWideNorm[n]));
+ xtermCloseXft(screen, &(screen->renderWideBold[n]));
+ xtermCloseXft(screen, &(screen->renderWideItal[n]));
+#endif
+ }
+ }
+}
+#endif
+
+/*
+ * Compute useful values for the font/window sizes
+ */
+void
+xtermComputeFontInfo(XtermWidget xw,
+ VTwin * win,
+ XFontStruct * font,
+ int sbwidth)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ int i, j, width, height;
+#if OPT_RENDERFONT
+ int fontnum = screen->menu_font_number;
+#endif
+
+#if OPT_RENDERFONT
+ /*
+ * xterm contains a lot of references to fonts, assuming they are fixed
+ * size. This chunk of code overrides the actual font-selection (see
+ * drawXtermText()), if the user has selected render-font. All of the
+ * font-loading for fixed-fonts still goes on whether or not this chunk
+ * overrides it.
+ */
+ if (UsingRenderFont(xw) && fontnum >= 0) {
+ String face_name = getFaceName(xw, False);
+ XftFont *norm = screen->renderFontNorm[fontnum].font;
+ XftFont *bold = screen->renderFontBold[fontnum].font;
+ XftFont *ital = screen->renderFontItal[fontnum].font;
+#if OPT_RENDERWIDE
+ XftFont *wnorm = screen->renderWideNorm[fontnum].font;
+ XftFont *wbold = screen->renderWideBold[fontnum].font;
+ XftFont *wital = screen->renderWideItal[fontnum].font;
+#endif
+
+ if (norm == 0 && face_name) {
+ XftPattern *pat;
+ double face_size;
+
+ TRACE(("xtermComputeFontInfo font %d: norm(face %s, size %.1f)\n",
+ fontnum, face_name,
+ xw->misc.face_size[fontnum]));
+
+ fillInFaceSize(xw, fontnum);
+ face_size = xw->misc.face_size[fontnum];
+
+ /*
+ * By observation (there is no documentation), XftPatternBuild is
+ * cumulative. Build the bold- and italic-patterns on top of the
+ * normal pattern.
+ */
+#define NormXftPattern \
+ XFT_FAMILY, XftTypeString, "mono", \
+ XFT_SIZE, XftTypeDouble, face_size, \
+ XFT_SPACING, XftTypeInteger, XFT_MONO
+
+#define BoldXftPattern(norm) \
+ XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD, \
+ XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
+
+#define ItalXftPattern(norm) \
+ XFT_SLANT, XftTypeInteger, XFT_SLANT_ITALIC, \
+ XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
+
+ if ((pat = XftNameParse(face_name)) != 0) {
+#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
+ XftPatternBuild(pat,
+ NormXftPattern,
+ (void *) 0);
+ norm = OPEN_XFT("normal");
+
+ if (norm != 0) {
+ XftPatternBuild(pat,
+ BoldXftPattern(norm),
+ (void *) 0);
+ bold = OPEN_XFT("bold");
+
+#if OPT_ISO_COLORS
+ if (screen->italicULMode
+ && (pat = XftNameParse(face_name)) != 0) {
+ XftPatternBuild(pat,
+ NormXftPattern,
+ ItalXftPattern(norm),
+ (void *) 0);
+ ital = OPEN_XFT("italic");
+ }
+#endif /* OPT_ISO_COLORS */
+#undef OPEN_XFT
+
+ /*
+ * FIXME: just assume that the corresponding font has no
+ * graphics characters.
+ */
+ if (screen->fnt_boxes) {
+ screen->fnt_boxes = False;
+ TRACE(("Xft opened - will %suse internal line-drawing characters\n",
+ screen->fnt_boxes ? "not " : ""));
+ }
+ }
+
+ XftPatternDestroy(pat);
+ }
+
+ CACHE_XFT(screen->renderFontNorm, norm);
+ CACHE_XFT(screen->renderFontBold, bold);
+ CACHE_XFT(screen->renderFontItal, ital);
+
+ /*
+ * See xtermXftDrawString().
+ */
+#if OPT_RENDERWIDE
+ if (norm != 0 && screen->wide_chars) {
+ int char_width = norm->max_advance_width * 2;
+#ifdef FC_ASPECT
+ double aspect = ((xw->misc.face_wide_name
+ || screen->renderFontNorm[fontnum].map.mixed)
+ ? 1.0
+ : 2.0);
+#endif
+
+ face_name = getFaceName(xw, True);
+ TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
+ NonNull(face_name),
+ char_width));
+
+#define WideXftPattern \
+ XFT_FAMILY, XftTypeString, "mono", \
+ XFT_SIZE, XftTypeDouble, face_size, \
+ XFT_SPACING, XftTypeInteger, XFT_MONO
+
+ if (face_name && (pat = XftNameParse(face_name)) != 0) {
+#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
+ XftPatternBuild(pat,
+ WideXftPattern,
+ XFT_CHAR_WIDTH, XftTypeInteger, char_width,
+#ifdef FC_ASPECT
+ FC_ASPECT, XftTypeDouble, aspect,
+#endif
+ (void *) 0);
+ wnorm = OPEN_XFT("wide");
+
+ if (wnorm != 0) {
+ XftPatternBuild(pat,
+ WideXftPattern,
+ BoldXftPattern(wnorm),
+ (void *) 0);
+ wbold = OPEN_XFT("wide-bold");
+
+#if OPT_ISO_COLORS
+ if (screen->italicULMode
+ && (pat = XftNameParse(face_name)) != 0) {
+ XftPatternBuild(pat,
+ WideXftPattern,
+ ItalXftPattern(wnorm),
+ (void *) 0);
+ wital = OPEN_XFT("wide-italic");
+ }
+#endif
+#undef OPEN_XFT
+ }
+ XftPatternDestroy(pat);
+ }
+
+ CACHE_XFT(screen->renderWideNorm, wnorm);
+ CACHE_XFT(screen->renderWideBold, wbold);
+ CACHE_XFT(screen->renderWideItal, wital);
+ }
+#endif /* OPT_RENDERWIDE */
+ }
+ if (norm == 0) {
+ TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum));
+ xw->misc.render_font = False;
+ update_font_renderfont();
+ /* now we will fall through into the bitmap fonts */
+ } else {
+ setRenderFontsize(screen, win, norm, NULL);
+ setRenderFontsize(screen, win, bold, "bold");
+ setRenderFontsize(screen, win, ital, "ital");
+#if OPT_BOX_CHARS
+ setupPackedFonts(xw);
+
+ if (screen->force_packed) {
+ XTermXftFonts *use = &(screen->renderFontNorm[fontnum]);
+ SetFontHeight(screen, win, use->font->ascent + use->font->descent);
+ SetFontWidth(screen, win, use->map.min_width);
+ TRACE(("...packed TrueType font %dx%d vs %d\n",
+ win->f_height,
+ win->f_width,
+ use->map.max_width));
+ }
+#endif
+ DUMP_XFT(xw, &(screen->renderFontNorm[fontnum]));
+ }
+ }
+ /*
+ * Are we handling a bitmap font?
+ */
+ else
+#endif /* OPT_RENDERFONT */
+ {
+ if (is_double_width_font(font) && !(screen->fnt_prop)) {
+ SetFontWidth(screen, win, font->min_bounds.width);
+ } else {
+ SetFontWidth(screen, win, font->max_bounds.width);
+ }
+ SetFontHeight(screen, win, font->ascent + font->descent);
+ win->f_ascent = font->ascent;
+ win->f_descent = font->descent;
+ }
+ i = 2 * screen->border + sbwidth;
+ j = 2 * screen->border;
+ width = MaxCols(screen) * win->f_width + i;
+ height = MaxRows(screen) * win->f_height + j;
+ win->fullwidth = (Dimension) width;
+ win->fullheight = (Dimension) height;
+ win->width = width - i;
+ win->height = height - j;
+
+ TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
+ win->height,
+ win->width,
+ win->fullheight,
+ win->fullwidth,
+ win->f_height,
+ win->f_width,
+ win->f_ascent,
+ win->f_descent));
+
+ checkFontInfo(win->f_height, "height");
+ checkFontInfo(win->f_width, "width");
+}
+
+/* save this information as a side-effect for double-sized characters */
+void
+xtermSaveFontInfo(TScreen * screen, XFontStruct * font)
+{
+ screen->fnt_wide = (Dimension) (font->max_bounds.width);
+ screen->fnt_high = (Dimension) (font->ascent + font->descent);
+ TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
+}
+
+/*
+ * After loading a new font, update the structures that use its size.
+ */
+void
+xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ int scrollbar_width;
+ VTwin *win = &(screen->fullVwin);
+
+ scrollbar_width = (xw->misc.scrollbar
+ ? (screen->scrollWidget->core.width +
+ BorderWidth(screen->scrollWidget))
+ : 0);
+ xtermComputeFontInfo(xw, win, screen->fnts[fNorm].fs, scrollbar_width);
+ xtermSaveFontInfo(screen, screen->fnts[fNorm].fs);
+
+ if (doresize) {
+ if (VWindow(screen)) {
+ xtermClear(xw);
+ }
+ TRACE(("xtermUpdateFontInfo {{\n"));
+ DoResizeScreen(xw); /* set to the new natural size */
+ ResizeScrollBar(xw);
+ Redraw();
+ TRACE(("... }} xtermUpdateFontInfo\n"));
+#ifdef SCROLLBAR_RIGHT
+ updateRightScrollbar(xw);
+#endif
+ }
+ xtermSetCursorBox(screen);
+}
+
+#if OPT_BOX_CHARS
+
+/*
+ * Returns true if the given character is missing from the specified font.
+ */
+Bool
+xtermMissingChar(unsigned ch, XTermFonts * font)
+{
+ Bool result = False;
+ XFontStruct *fs = font->fs;
+ static XCharStruct dft, *tmp = &dft, *pc = 0;
+
+ if (fs->max_byte1 == 0) {
+#if OPT_WIDE_CHARS
+ if (ch > 255) {
+ TRACE(("xtermMissingChar %#04x (row)\n", ch));
+ return True;
+ }
+#endif
+ CI_GET_CHAR_INFO_1D(fs, E2A(ch), tmp, pc);
+ }
+#if OPT_WIDE_CHARS
+ else {
+ CI_GET_CHAR_INFO_2D(fs, HI_BYTE(ch), LO_BYTE(ch), tmp, pc);
+ }
+#else
+
+ if (!pc)
+ return False; /* Urgh! */
+#endif
+
+ if (CI_NONEXISTCHAR(pc)) {
+ TRACE(("xtermMissingChar %#04x (!exists)\n", ch));
+ result = True;
+ }
+ if (ch < 256) {
+ font->known_missing[ch] = (Char) (result ? 2 : 1);
+ }
+ return result;
+}
+
+/*
+ * The grid is arbitrary, enough resolution that nothing's lost in
+ * initialization.
+ */
+#define BOX_HIGH 60
+#define BOX_WIDE 60
+
+#define MID_HIGH (BOX_HIGH/2)
+#define MID_WIDE (BOX_WIDE/2)
+
+#define CHR_WIDE ((9*BOX_WIDE)/10)
+#define CHR_HIGH ((9*BOX_HIGH)/10)
+
+/*
+ * ...since we'll scale the values anyway.
+ */
+#define SCALED_X(n) ((int)(n) * (((int) font_width) - 1)) / (BOX_WIDE-1)
+#define SCALED_Y(n) ((int)(n) * (((int) font_height) - 1)) / (BOX_HIGH-1)
+#define SCALE_X(n) n = SCALED_X(n)
+#define SCALE_Y(n) n = SCALED_Y(n)
+
+#define SEG(x0,y0,x1,y1) x0,y0, x1,y1
+
+/*
+ * Draw the given graphic character, if it is simple enough (i.e., a
+ * line-drawing character).
+ */
+void
+xtermDrawBoxChar(XtermWidget xw,
+ unsigned ch,
+ unsigned flags,
+ GC gc,
+ int x,
+ int y,
+ int cells)
+{
+ TScreen *screen = TScreenOf(xw);
+ /* *INDENT-OFF* */
+ static const short glyph_ht[] = {
+ SEG(1*BOX_WIDE/10, 0, 1*BOX_WIDE/10,5*MID_HIGH/6), /* H */
+ SEG(6*BOX_WIDE/10, 0, 6*BOX_WIDE/10,5*MID_HIGH/6),
+ SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
+ SEG(2*BOX_WIDE/10, MID_HIGH, CHR_WIDE, MID_HIGH), /* T */
+ SEG(6*BOX_WIDE/10, MID_HIGH, 6*BOX_WIDE/10, CHR_HIGH),
+ -1
+ }, glyph_ff[] = {
+ SEG(1*BOX_WIDE/10, 0, 6*BOX_WIDE/10, 0), /* F */
+ SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
+ SEG(1*BOX_WIDE/10, 0, 0*BOX_WIDE/3, 5*MID_HIGH/6),
+ SEG(1*BOX_WIDE/3, MID_HIGH, CHR_WIDE, MID_HIGH), /* F */
+ SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
+ SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH),
+ -1
+ }, glyph_lf[] = {
+ SEG(1*BOX_WIDE/10, 0, 1*BOX_WIDE/10,9*MID_HIGH/12), /* L */
+ SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
+ SEG(1*BOX_WIDE/3, MID_HIGH, CHR_WIDE, MID_HIGH), /* F */
+ SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
+ SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH),
+ -1
+ }, glyph_nl[] = {
+ SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10, 0), /* N */
+ SEG(1*BOX_WIDE/10, 0, 5*BOX_WIDE/6, 5*MID_HIGH/6),
+ SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6, 0),
+ SEG(1*BOX_WIDE/3, MID_HIGH, 1*BOX_WIDE/3, CHR_HIGH), /* L */
+ SEG(1*BOX_WIDE/3, CHR_HIGH, CHR_WIDE, CHR_HIGH),
+ -1
+ }, glyph_vt[] = {
+ SEG(1*BOX_WIDE/10, 0, 5*BOX_WIDE/12,5*MID_HIGH/6), /* V */
+ SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6, 0),
+ SEG(2*BOX_WIDE/10, MID_HIGH, CHR_WIDE, MID_HIGH), /* T */
+ SEG(6*BOX_WIDE/10, MID_HIGH, 6*BOX_WIDE/10, CHR_HIGH),
+ -1
+ }, plus_or_minus[] =
+ {
+ SEG( 0, 5*BOX_HIGH/6, CHR_WIDE, 5*BOX_HIGH/6),
+ SEG( MID_WIDE, 2*BOX_HIGH/6, MID_WIDE, 4*BOX_HIGH/6),
+ SEG( 0, 3*BOX_HIGH/6, CHR_WIDE, 3*BOX_HIGH/6),
+ -1
+ }, lower_right_corner[] =
+ {
+ SEG( 0, MID_HIGH, MID_WIDE, MID_HIGH),
+ SEG( MID_WIDE, MID_HIGH, MID_WIDE, 0),
+ -1
+ }, upper_right_corner[] =
+ {
+ SEG( 0, MID_HIGH, MID_WIDE, MID_HIGH),
+ SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH),
+ -1
+ }, upper_left_corner[] =
+ {
+ SEG( MID_WIDE, MID_HIGH, BOX_WIDE, MID_HIGH),
+ SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH),
+ -1
+ }, lower_left_corner[] =
+ {
+ SEG( MID_WIDE, 0, MID_WIDE, MID_HIGH),
+ SEG( MID_WIDE, MID_WIDE, BOX_WIDE, MID_HIGH),
+ -1
+ }, cross[] =
+ {
+ SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
+ SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
+ -1
+ }, scan_line_1[] =
+ {
+ SEG( 0, 0, BOX_WIDE, 0),
+ -1
+ }, scan_line_3[] =
+ {
+ SEG( 0, BOX_HIGH/4, BOX_WIDE, BOX_HIGH/4),
+ -1
+ }, scan_line_7[] =
+ {
+ SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
+ -1
+ }, scan_line_9[] =
+ {
+ SEG( 0, 3*BOX_HIGH/4, BOX_WIDE, 3*BOX_HIGH/4),
+ -1
+ }, horizontal_line[] =
+ {
+ SEG( 0, BOX_HIGH, BOX_WIDE, BOX_HIGH),
+ -1
+ }, left_tee[] =
+ {
+ SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
+ SEG( MID_WIDE, MID_HIGH, BOX_WIDE, MID_HIGH),
+ -1
+ }, right_tee[] =
+ {
+ SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
+ SEG( MID_WIDE, MID_HIGH, 0, MID_HIGH),
+ -1
+ }, bottom_tee[] =
+ {
+ SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
+ SEG( MID_WIDE, 0, MID_WIDE, MID_HIGH),
+ -1
+ }, top_tee[] =
+ {
+ SEG( 0, MID_HIGH, BOX_WIDE, MID_HIGH),
+ SEG( MID_WIDE, MID_HIGH, MID_WIDE, BOX_HIGH),
+ -1
+ }, vertical_line[] =
+ {
+ SEG( MID_WIDE, 0, MID_WIDE, BOX_HIGH),
+ -1
+ }, less_than_or_equal[] =
+ {
+ SEG( CHR_WIDE, BOX_HIGH/3, 0, MID_HIGH),
+ SEG( CHR_WIDE, 2*BOX_HIGH/3, 0, MID_HIGH),
+ SEG( 0, 3*BOX_HIGH/4, CHR_WIDE, 3*BOX_HIGH/4),
+ -1
+ }, greater_than_or_equal[] =
+ {
+ SEG( 0, BOX_HIGH/3, CHR_WIDE, MID_HIGH),
+ SEG( 0, 2*BOX_HIGH/3, CHR_WIDE, MID_HIGH),
+ SEG( 0, 3*BOX_HIGH/4, CHR_WIDE, 3*BOX_HIGH/4),
+ -1
+ }, greek_pi[] =
+ {
+ SEG( 0, MID_HIGH, CHR_WIDE, MID_HIGH),
+ SEG(5*CHR_WIDE/6, MID_HIGH, 5*CHR_WIDE/6, CHR_HIGH),
+ SEG(2*CHR_WIDE/6, MID_HIGH, 2*CHR_WIDE/6, CHR_HIGH),
+ -1
+ }, not_equal_to[] =
+ {
+ SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3, CHR_HIGH),
+ SEG( 0, 2*BOX_HIGH/3, CHR_WIDE, 2*BOX_HIGH/3),
+ SEG( 0, MID_HIGH, CHR_WIDE, MID_HIGH),
+ -1
+ };
+ /* *INDENT-ON* */
+
+ static const short *lines[] =
+ {
+ 0, /* 00 (unused) */
+ 0, /* 01 diamond */
+ 0, /* 02 box */
+ glyph_ht, /* 03 HT */
+ glyph_ff, /* 04 FF */
+ 0, /* 05 CR */
+ glyph_lf, /* 06 LF */
+ 0, /* 07 degrees (small circle) */
+ plus_or_minus, /* 08 */
+ glyph_nl, /* 09 */
+ glyph_vt, /* 0A */
+ lower_right_corner, /* 0B */
+ upper_right_corner, /* 0C */
+ upper_left_corner, /* 0D */
+ lower_left_corner, /* 0E */
+ cross, /* 0F */
+ scan_line_1, /* 10 */
+ scan_line_3, /* 11 */
+ scan_line_7, /* 12 */
+ scan_line_9, /* 13 */
+ horizontal_line, /* 14 */
+ left_tee, /* 15 */
+ right_tee, /* 16 */
+ bottom_tee, /* 17 */
+ top_tee, /* 18 */
+ vertical_line, /* 19 */
+ less_than_or_equal, /* 1A */
+ greater_than_or_equal, /* 1B */
+ greek_pi, /* 1C */
+ not_equal_to, /* 1D */
+ 0, /* 1E LB */
+ 0, /* 1F bullet */
+ };
+
+ GC gc2;
+ CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
+ VTwin *cgsWin = WhichVWin(screen);
+ const short *p;
+ unsigned font_width = (unsigned) (((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide);
+ unsigned font_height = (unsigned) (((flags & DOUBLEHFONT) ? 2 : 1) * screen->fnt_high);
+
+ if (cells > 1)
+ font_width *= (unsigned) cells;
+
+#if OPT_WIDE_CHARS
+ /*
+ * Try to show line-drawing characters if we happen to be in UTF-8
+ * mode, but have gotten an old-style font.
+ */
+ if (screen->utf8_mode
+#if OPT_RENDERFONT
+ && !UsingRenderFont(xw)
+#endif
+ && (ch > 127)
+ && (ch != UCS_REPL)) {
+ unsigned n;
+ for (n = 1; n < 32; n++) {
+ if (dec2ucs(n) == ch
+ && !((flags & BOLD)
+ ? IsXtermMissingChar(screen, n, &screen->fnts[fBold])
+ : IsXtermMissingChar(screen, n, &screen->fnts[fNorm]))) {
+ TRACE(("...use xterm-style linedrawing\n"));
+ ch = n;
+ break;
+ }
+ }
+ }
+#endif
+
+ TRACE(("DRAW_BOX(%d) cell %dx%d at %d,%d%s\n",
+ ch, font_height, font_width, y, x,
+ (ch >= (sizeof(lines) / sizeof(lines[0]))
+ ? "-BAD"
+ : "")));
+
+ if (cgsId == gcDots) {
+ setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
+ setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
+ setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
+ } else {
+ setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
+ setCgsFore(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
+ setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
+ }
+ gc2 = getCgsGC(xw, cgsWin, cgsId);
+
+ if (!(flags & NOBACKGROUND)) {
+ XFillRectangle(screen->display, VWindow(screen), gc2, x, y,
+ font_width,
+ font_height);
+ }
+
+ setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
+ setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
+ setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
+ gc2 = getCgsGC(xw, cgsWin, cgsId);
+
+ XSetLineAttributes(screen->display, gc2,
+ (flags & BOLD)
+ ? ((font_height > 12)
+ ? font_height / 12
+ : 1)
+ : ((font_height > 16)
+ ? font_height / 16
+ : 1),
+ LineSolid,
+ CapProjecting,
+ JoinMiter);
+
+ if (ch == 1) { /* diamond */
+ XPoint points[5];
+ int npoints = 5, n;
+
+ points[0].x = MID_WIDE;
+ points[0].y = BOX_HIGH / 4;
+
+ points[1].x = 8 * BOX_WIDE / 8;
+ points[1].y = MID_HIGH;
+
+ points[2].x = points[0].x;
+ points[2].y = 3 * BOX_HIGH / 4;
+
+ points[3].x = 0 * BOX_WIDE / 8;
+ points[3].y = points[1].y;
+
+ points[4].x = points[0].x;
+ points[4].y = points[0].y;
+
+ for (n = 0; n < npoints; ++n) {
+ points[n].x = (short) SCALED_X(points[n].x);
+ points[n].y = (short) SCALED_Y(points[n].y);
+ points[n].x = (short) (points[n].x + x);
+ points[n].y = (short) (points[n].y + y);
+ }
+
+ XFillPolygon(screen->display,
+ VWindow(screen), gc2,
+ points, npoints,
+ Convex, CoordModeOrigin);
+ } else if (ch == 7) { /* degrees */
+ unsigned width = (BOX_WIDE / 3);
+ int x_coord = MID_WIDE - (int) (width / 2);
+ int y_coord = MID_HIGH - (int) width;
+
+ SCALE_X(x_coord);
+ SCALE_Y(y_coord);
+ width = (unsigned) SCALED_X(width);
+
+ XDrawArc(screen->display,
+ VWindow(screen), gc2,
+ x + x_coord, y + y_coord, width, width,
+ 0,
+ 360 * 64);
+ } else if (ch == 0x1f) { /* bullet */
+ unsigned width = 7 * BOX_WIDE / 10;
+ int x_coord = MID_WIDE - (int) (width / 3);
+ int y_coord = MID_HIGH - (int) (width / 3);
+
+ SCALE_X(x_coord);
+ SCALE_Y(y_coord);
+ width = (unsigned) SCALED_X(width);
+
+ XDrawArc(screen->display,
+ VWindow(screen), gc2,
+ x + x_coord, y + y_coord, width, width,
+ 0,
+ 360 * 64);
+ } else if (ch < (sizeof(lines) / sizeof(lines[0]))
+ && (p = lines[ch]) != 0) {
+ int coord[4];
+ int n = 0;
+ while (*p >= 0) {
+ coord[n++] = *p++;
+ if (n == 4) {
+ SCALE_X(coord[0]);
+ SCALE_Y(coord[1]);
+ SCALE_X(coord[2]);
+ SCALE_Y(coord[3]);
+ XDrawLine(screen->display,
+ VWindow(screen), gc2,
+ x + coord[0], y + coord[1],
+ x + coord[2], y + coord[3]);
+ n = 0;
+ }
+ }
+ } else if (screen->force_all_chars) {
+ /* bounding rectangle, for debugging */
+ XDrawRectangle(screen->display, VWindow(screen), gc2, x, y,
+ font_width - 1,
+ font_height - 1);
+ }
+}
+
+#if OPT_RENDERFONT
+
+/*
+ * Check if the given character has a glyph known to Xft.
+ *
+ * see xc/lib/Xft/xftglyphs.c
+ */
+Bool
+xtermXftMissing(XtermWidget xw, XftFont * font, unsigned wc)
+{
+ Bool result = False;
+
+ if (font != 0) {
+ TScreen *screen = TScreenOf(xw);
+ if (!XftGlyphExists(screen->display, font, wc)) {
+#if OPT_WIDE_CHARS
+ TRACE(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n",
+ wc, ucs2dec(wc), dec2ucs(wc)));
+#else
+ TRACE(("xtermXftMissing %d\n", wc));
+#endif
+ result = True;
+ }
+ }
+ return result;
+}
+#endif /* OPT_RENDERFONT && OPT_WIDE_CHARS */
+
+#endif /* OPT_BOX_CHARS */
+
+#if OPT_WIDE_CHARS
+#define MY_UCS(ucs,dec) case ucs: result = dec; break
+unsigned
+ucs2dec(unsigned ch)
+{
+ unsigned result = ch;
+ if ((ch > 127)
+ && (ch != UCS_REPL)) {
+ switch (ch) {
+ MY_UCS(0x25ae, 0); /* black vertical rectangle */
+ MY_UCS(0x25c6, 1); /* black diamond */
+ MY_UCS(0x2592, 2); /* medium shade */
+ MY_UCS(0x2409, 3); /* symbol for horizontal tabulation */
+ MY_UCS(0x240c, 4); /* symbol for form feed */
+ MY_UCS(0x240d, 5); /* symbol for carriage return */
+ MY_UCS(0x240a, 6); /* symbol for line feed */
+ MY_UCS(0x00b0, 7); /* degree sign */
+ MY_UCS(0x00b1, 8); /* plus-minus sign */
+ MY_UCS(0x2424, 9); /* symbol for newline */
+ MY_UCS(0x240b, 10); /* symbol for vertical tabulation */
+ MY_UCS(0x2518, 11); /* box drawings light up and left */
+ MY_UCS(0x2510, 12); /* box drawings light down and left */
+ MY_UCS(0x250c, 13); /* box drawings light down and right */
+ MY_UCS(0x2514, 14); /* box drawings light up and right */
+ MY_UCS(0x253c, 15); /* box drawings light vertical and horizontal */
+ MY_UCS(0x23ba, 16); /* box drawings scan 1 */
+ MY_UCS(0x23bb, 17); /* box drawings scan 3 */
+ MY_UCS(0x2500, 18); /* box drawings light horizontal */
+ MY_UCS(0x23bc, 19); /* box drawings scan 7 */
+ MY_UCS(0x23bd, 20); /* box drawings scan 9 */
+ MY_UCS(0x251c, 21); /* box drawings light vertical and right */
+ MY_UCS(0x2524, 22); /* box drawings light vertical and left */
+ MY_UCS(0x2534, 23); /* box drawings light up and horizontal */
+ MY_UCS(0x252c, 24); /* box drawings light down and horizontal */
+ MY_UCS(0x2502, 25); /* box drawings light vertical */
+ MY_UCS(0x2264, 26); /* less-than or equal to */
+ MY_UCS(0x2265, 27); /* greater-than or equal to */
+ MY_UCS(0x03c0, 28); /* greek small letter pi */
+ MY_UCS(0x2260, 29); /* not equal to */
+ MY_UCS(0x00a3, 30); /* pound sign */
+ MY_UCS(0x00b7, 31); /* middle dot */
+ }
+ }
+ return result;
+}
+
+#undef MY_UCS
+#define MY_UCS(ucs,dec) case dec: result = ucs; break
+
+unsigned
+dec2ucs(unsigned ch)
+{
+ unsigned result = ch;
+ if (xtermIsDecGraphic(ch)) {
+ switch (ch) {
+ MY_UCS(0x25ae, 0); /* black vertical rectangle */
+ MY_UCS(0x25c6, 1); /* black diamond */
+ MY_UCS(0x2592, 2); /* medium shade */
+ MY_UCS(0x2409, 3); /* symbol for horizontal tabulation */
+ MY_UCS(0x240c, 4); /* symbol for form feed */
+ MY_UCS(0x240d, 5); /* symbol for carriage return */
+ MY_UCS(0x240a, 6); /* symbol for line feed */
+ MY_UCS(0x00b0, 7); /* degree sign */
+ MY_UCS(0x00b1, 8); /* plus-minus sign */
+ MY_UCS(0x2424, 9); /* symbol for newline */
+ MY_UCS(0x240b, 10); /* symbol for vertical tabulation */
+ MY_UCS(0x2518, 11); /* box drawings light up and left */
+ MY_UCS(0x2510, 12); /* box drawings light down and left */
+ MY_UCS(0x250c, 13); /* box drawings light down and right */
+ MY_UCS(0x2514, 14); /* box drawings light up and right */
+ MY_UCS(0x253c, 15); /* box drawings light vertical and horizontal */
+ MY_UCS(0x23ba, 16); /* box drawings scan 1 */
+ MY_UCS(0x23bb, 17); /* box drawings scan 3 */
+ MY_UCS(0x2500, 18); /* box drawings light horizontal */
+ MY_UCS(0x23bc, 19); /* box drawings scan 7 */
+ MY_UCS(0x23bd, 20); /* box drawings scan 9 */
+ MY_UCS(0x251c, 21); /* box drawings light vertical and right */
+ MY_UCS(0x2524, 22); /* box drawings light vertical and left */
+ MY_UCS(0x2534, 23); /* box drawings light up and horizontal */
+ MY_UCS(0x252c, 24); /* box drawings light down and horizontal */
+ MY_UCS(0x2502, 25); /* box drawings light vertical */
+ MY_UCS(0x2264, 26); /* less-than or equal to */
+ MY_UCS(0x2265, 27); /* greater-than or equal to */
+ MY_UCS(0x03c0, 28); /* greek small letter pi */
+ MY_UCS(0x2260, 29); /* not equal to */
+ MY_UCS(0x00a3, 30); /* pound sign */
+ MY_UCS(0x00b7, 31); /* middle dot */
+ }
+ }
+ return result;
+}
+
+#endif /* OPT_WIDE_CHARS */
+
+#if OPT_SHIFT_FONTS
+static int
+lookupOneFontSize(XtermWidget xw, int fontnum)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ if (screen->menu_font_sizes[fontnum] == 0) {
+ XTermFonts fnt;
+
+ memset(&fnt, 0, sizeof(fnt));
+ screen->menu_font_sizes[fontnum] = -1;
+ if (xtermOpenFont(xw,
+ screen->MenuFontName(fontnum),
+ &fnt,
+ ((fontnum <= fontMenu_lastBuiltin)
+ ? fwAlways
+ : fwResource),
+ True)) {
+ if (fontnum <= fontMenu_lastBuiltin
+ || strcmp(fnt.fn, DEFFONT)) {
+ screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
+ if (screen->menu_font_sizes[fontnum] <= 0)
+ screen->menu_font_sizes[fontnum] = -1;
+ }
+ xtermCloseFont(xw, &fnt);
+ }
+ }
+ return (screen->menu_font_sizes[fontnum] > 0);
+}
+
+/*
+ * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
+ */
+static void
+lookupFontSizes(XtermWidget xw)
+{
+ int n;
+
+ for (n = 0; n < NMENUFONTS; n++) {
+ (void) lookupOneFontSize(xw, n);
+ }
+}
+
+#if OPT_RENDERFONT
+static void
+fillInFaceSize(XtermWidget xw, int fontnum)
+{
+ TScreen *screen = TScreenOf(xw);
+ float value;
+ double face_size = xw->misc.face_size[fontnum];
+
+ if (face_size <= 0.0) {
+#if OPT_SHIFT_FONTS
+ /*
+ * If the user is switching font-sizes, make it follow by
+ * default the same ratios to the default as the fixed fonts
+ * would, for easy comparison. There will be some differences
+ * since the fixed fonts have a variety of height/width ratios,
+ * but this is simpler than adding another resource value - and
+ * as noted above, the data for the fixed fonts are available.
+ */
+ (void) lookupOneFontSize(xw, 0);
+ if (fontnum == fontMenu_default) {
+ sscanf(DEFFACESIZE, "%f", &value);
+ face_size = value;
+ } else if (lookupOneFontSize(xw, fontnum)
+ && (screen->menu_font_sizes[0]
+ != screen->menu_font_sizes[fontnum])) {
+ double ratio;
+ long num = screen->menu_font_sizes[fontnum];
+ long den = screen->menu_font_sizes[0];
+
+ if (den <= 0)
+ den = 1;
+ ratio = dimSquareRoot((double) num / (double) den);
+
+ face_size = (ratio * xw->misc.face_size[0]);
+ TRACE(("scaled[%d] using %3ld/%ld = %.2f -> %f\n",
+ fontnum, num, den, ratio, face_size));
+ } else
+#endif
+ {
+#define LikeBitmap(s) (((s) / 78.0) * xw->misc.face_size[fontMenu_default])
+ switch (fontnum) {
+ case fontMenu_font1:
+ face_size = LikeBitmap(2.0);
+ break;
+ case fontMenu_font2:
+ face_size = LikeBitmap(35.0);
+ break;
+ case fontMenu_font3:
+ face_size = LikeBitmap(60.0);
+ break;
+ default:
+ sscanf(DEFFACESIZE, "%f", &value);
+ face_size = value;
+ break;
+ case fontMenu_font4:
+ face_size = LikeBitmap(90.0);
+ break;
+ case fontMenu_font5:
+ face_size = LikeBitmap(135.0);
+ break;
+ case fontMenu_font6:
+ face_size = LikeBitmap(200.0);
+ break;
+ }
+ TRACE(("builtin[%d] -> %f\n", fontnum, face_size));
+ }
+ xw->misc.face_size[fontnum] = (float) face_size;
+ }
+}
+
+/* no selection or escape */
+#define NMENU_RENDERFONTS (fontMenu_lastBuiltin + 1)
+
+/*
+ * Workaround for breakage in font-packages - check if all of the bitmap font
+ * sizes are the same, and if we're using TrueType fonts.
+ */
+static Boolean
+useFaceSizes(XtermWidget xw)
+{
+ Boolean result = False;
+ int n;
+
+ TRACE(("useFaceSizes {{\n"));
+ if (UsingRenderFont(xw)) {
+ Boolean nonzero = True;
+
+ for (n = 0; n < NMENU_RENDERFONTS; ++n) {
+ if (xw->misc.face_size[n] <= 0.0) {
+ nonzero = False;
+ break;
+ }
+ }
+ if (!nonzero) {
+ Boolean broken_fonts = True;
+ TScreen *screen = TScreenOf(xw);
+ long first;
+
+ lookupFontSizes(xw);
+ first = screen->menu_font_sizes[0];
+ for (n = 0; n < NMENUFONTS; n++) {
+ if (screen->menu_font_sizes[n] > 0
+ && screen->menu_font_sizes[n] != first) {
+ broken_fonts = False;
+ break;
+ }
+ }
+
+ if (broken_fonts) {
+
+ TRACE(("bitmap fonts are broken - set faceSize resources\n"));
+ for (n = 0; n < NMENUFONTS; n++) {
+ fillInFaceSize(xw, n);
+ }
+
+ }
+ }
+ result = True;
+ }
+ TRACE(("...}}useFaceSizes %d\n", result));
+ return result;
+}
+#endif /* OPT_RENDERFONT */
+
+/*
+ * Find the index of a larger/smaller font (according to the sign of 'relative'
+ * and its magnitude), starting from the 'old' index.
+ */
+int
+lookupRelativeFontSize(XtermWidget xw, int old, int relative)
+{
+ TScreen *screen = TScreenOf(xw);
+ int n, m = -1;
+
+ TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative));
+ if (!IsIcon(screen)) {
+#if OPT_RENDERFONT
+ if (useFaceSizes(xw)) {
+ TRACE(("...using FaceSize\n"));
+ if (relative != 0) {
+ for (n = 0; n < NMENU_RENDERFONTS; ++n) {
+ fillInFaceSize(xw, n);
+ if (xw->misc.face_size[n] > 0 &&
+ xw->misc.face_size[n] != xw->misc.face_size[old]) {
+ int cmp_0 = ((xw->misc.face_size[n] >
+ xw->misc.face_size[old])
+ ? relative
+ : -relative);
+ int cmp_m = ((m < 0)
+ ? 1
+ : ((xw->misc.face_size[n] <
+ xw->misc.face_size[m])
+ ? relative
+ : -relative));
+ if (cmp_0 > 0 && cmp_m > 0) {
+ m = n;
+ }
+ }
+ }
+ }
+ } else
+#endif
+ {
+ TRACE(("...using bitmap areas\n"));
+ lookupFontSizes(xw);
+ if (relative != 0) {
+ for (n = 0; n < NMENUFONTS; ++n) {
+ if (screen->menu_font_sizes[n] > 0 &&
+ screen->menu_font_sizes[n] !=
+ screen->menu_font_sizes[old]) {
+ int cmp_0 = ((screen->menu_font_sizes[n] >
+ screen->menu_font_sizes[old])
+ ? relative
+ : -relative);
+ int cmp_m = ((m < 0)
+ ? 1
+ : ((screen->menu_font_sizes[n] <
+ screen->menu_font_sizes[m])
+ ? relative
+ : -relative));
+ if (cmp_0 > 0 && cmp_m > 0) {
+ m = n;
+ }
+ }
+ }
+ }
+ }
+ TRACE(("...new index %d\n", m));
+ if (m >= 0) {
+ if (relative > 1)
+ m = lookupRelativeFontSize(xw, m, relative - 1);
+ else if (relative < -1)
+ m = lookupRelativeFontSize(xw, m, relative + 1);
+ }
+ }
+ return m;
+}
+
+/* ARGSUSED */
+void
+HandleLargerFont(Widget w GCC_UNUSED,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *param_count GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ TRACE(("Handle larger-vt-font for %p\n", (void *) w));
+ if ((xw = getXtermWidget(w)) != 0) {
+ if (xw->misc.shift_fonts) {
+ TScreen *screen = TScreenOf(xw);
+ int m;
+
+ m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
+ if (m >= 0) {
+ SetVTFont(xw, m, True, NULL);
+ } else {
+ Bell(xw, XkbBI_MinorError, 0);
+ }
+ }
+ }
+}
+
+/* ARGSUSED */
+void
+HandleSmallerFont(Widget w GCC_UNUSED,
+ XEvent * event GCC_UNUSED,
+ String * params GCC_UNUSED,
+ Cardinal *param_count GCC_UNUSED)
+{
+ XtermWidget xw;
+
+ TRACE(("Handle smaller-vt-font for %p\n", (void *) w));
+ if ((xw = getXtermWidget(w)) != 0) {
+ if (xw->misc.shift_fonts) {
+ TScreen *screen = TScreenOf(xw);
+ int m;
+
+ m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
+ if (m >= 0) {
+ SetVTFont(xw, m, True, NULL);
+ } else {
+ Bell(xw, XkbBI_MinorError, 0);
+ }
+ }
+ }
+}
+#endif
+
+int
+xtermGetFont(const char *param)
+{
+ int fontnum;
+
+ switch (param[0]) {
+ case 'd':
+ case 'D':
+ case '0':
+ fontnum = fontMenu_default;
+ break;
+ case '1':
+ fontnum = fontMenu_font1;
+ break;
+ case '2':
+ fontnum = fontMenu_font2;
+ break;
+ case '3':
+ fontnum = fontMenu_font3;
+ break;
+ case '4':
+ fontnum = fontMenu_font4;
+ break;
+ case '5':
+ fontnum = fontMenu_font5;
+ break;
+ case '6':
+ fontnum = fontMenu_font6;
+ break;
+ case 'e':
+ case 'E':
+ fontnum = fontMenu_fontescape;
+ break;
+ case 's':
+ case 'S':
+ fontnum = fontMenu_fontsel;
+ break;
+ default:
+ fontnum = -1;
+ break;
+ }
+ return fontnum;
+}
+
+/* ARGSUSED */
+void
+HandleSetFont(Widget w GCC_UNUSED,
+ XEvent * event GCC_UNUSED,
+ String * params,
+ Cardinal *param_count)
+{
+ XtermWidget xw;
+
+ if ((xw = getXtermWidget(w)) != 0) {
+ int fontnum;
+ VTFontNames fonts;
+
+ memset(&fonts, 0, sizeof(fonts));
+
+ if (*param_count == 0) {
+ fontnum = fontMenu_default;
+ } else {
+ Cardinal maxparams = 1; /* total number of params allowed */
+ int result = xtermGetFont(params[0]);
+
+ switch (result) {
+ case fontMenu_default: /* FALLTHRU */
+ case fontMenu_font1: /* FALLTHRU */
+ case fontMenu_font2: /* FALLTHRU */
+ case fontMenu_font3: /* FALLTHRU */
+ case fontMenu_font4: /* FALLTHRU */
+ case fontMenu_font5: /* FALLTHRU */
+ case fontMenu_font6: /* FALLTHRU */
+ break;
+ case fontMenu_fontescape:
+#if OPT_WIDE_CHARS
+ maxparams = 5;
+#else
+ maxparams = 3;
+#endif
+ break;
+ case fontMenu_fontsel:
+ maxparams = 2;
+ break;
+ default:
+ Bell(xw, XkbBI_MinorError, 0);
+ return;
+ }
+ fontnum = result;
+
+ if (*param_count > maxparams) { /* see if extra args given */
+ Bell(xw, XkbBI_MinorError, 0);
+ return;
+ }
+ switch (*param_count) { /* assign 'em */
+#if OPT_WIDE_CHARS
+ case 5:
+ fonts.f_wb = params[4];
+ /* FALLTHRU */
+ case 4:
+ fonts.f_w = params[3];
+ /* FALLTHRU */
+#endif
+ case 3:
+ fonts.f_b = params[2];
+ /* FALLTHRU */
+ case 2:
+ fonts.f_n = params[1];
+ break;
+ }
+ }
+
+ SetVTFont(xw, fontnum, True, &fonts);
+ }
+}
+
+void
+SetVTFont(XtermWidget xw,
+ int which,
+ Bool doresize,
+ const VTFontNames * fonts)
+{
+ TScreen *screen = TScreenOf(xw);
+
+ TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
+ (fonts && fonts->f_n) ? fonts->f_n : "<null>",
+ (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
+
+ if (IsIcon(screen)) {
+ Bell(xw, XkbBI_MinorError, 0);
+ } else if (which >= 0 && which < NMENUFONTS) {
+ VTFontNames myfonts;
+
+ memset(&myfonts, 0, sizeof(myfonts));
+ if (fonts != 0)
+ myfonts = *fonts;
+
+ if (which == fontMenu_fontsel) { /* go get the selection */
+ FindFontSelection(xw, myfonts.f_n, False);
+ } else {
+ int oldFont = screen->menu_font_number;
+
+#define USE_CACHED(field, name) \
+ if (myfonts.field == 0) { \
+ myfonts.field = x_strdup(screen->menu_font_names[which][name]); \
+ TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \
+ which, NonNull(myfonts.field))); \
+ } else { \
+ TRACE(("set myfonts." #field " reused\n")); \
+ }
+#define SAVE_FNAME(field, name) \
+ if (myfonts.field != 0) { \
+ if (screen->menu_font_names[which][name] == 0 \
+ || strcmp(screen->menu_font_names[which][name], myfonts.field)) { \
+ TRACE(("updating menu_font_names[%d][" #name "] to %s\n", \
+ which, myfonts.field)); \
+ screen->menu_font_names[which][name] = x_strdup(myfonts.field); \
+ } \
+ }
+
+ USE_CACHED(f_n, fNorm);
+ USE_CACHED(f_b, fBold);
+#if OPT_WIDE_CHARS
+ USE_CACHED(f_w, fWide);
+ USE_CACHED(f_wb, fWBold);
+#endif
+ if (xtermLoadFont(xw,
+ &myfonts,
+ doresize, which)) {
+ /*
+ * If successful, save the data so that a subsequent query via
+ * OSC-50 will return the expected values.
+ */
+ SAVE_FNAME(f_n, fNorm);
+ SAVE_FNAME(f_b, fBold);
+#if OPT_WIDE_CHARS
+ SAVE_FNAME(f_w, fWide);
+ SAVE_FNAME(f_wb, fWBold);
+#endif
+ } else {
+ xtermLoadFont(xw,
+ xtermFontName(screen->MenuFontName(oldFont)),
+ doresize, oldFont);
+ Bell(xw, XkbBI_MinorError, 0);
+ }
+ }
+ } else {
+ Bell(xw, XkbBI_MinorError, 0);
+ }
+ return;
+}