diff options
Diffstat (limited to 'fontutils.c')
-rw-r--r-- | fontutils.c | 3197 |
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; +} |