Home | History | Annotate | Line # | Download | only in dist
      1 /* $XTermId: fontutils.c,v 1.787 2024/12/01 20:29:42 tom Exp $ */
      2 
      3 /*
      4  * Copyright 1998-2023,2024 by Thomas E. Dickey
      5  *
      6  *                         All Rights Reserved
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining a
      9  * copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sublicense, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice shall be included
     17  * in all copies or substantial portions of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     22  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
     23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     26  *
     27  * Except as contained in this notice, the name(s) of the above copyright
     28  * holders shall not be used in advertising or otherwise to promote the
     29  * sale, use or other dealings in this Software without prior written
     30  * authorization.
     31  */
     32 
     33 /*
     34  * A portion of this module (for FontNameProperties) was adapted from EMU 1.3;
     35  * it constructs font names with specific properties changed, e.g., for bold
     36  * and double-size characters.
     37  */
     38 
     39 #define RES_OFFSET(field)	XtOffsetOf(SubResourceRec, field)
     40 
     41 #include <fontutils.h>
     42 #include <X11/Xmu/Drawing.h>
     43 #include <X11/Xmu/CharSet.h>
     44 
     45 #include <main.h>
     46 #include <data.h>
     47 #include <error.h>
     48 #include <menu.h>
     49 #include <xstrings.h>
     50 #include <xterm.h>
     51 
     52 #include <stdio.h>
     53 #include <ctype.h>
     54 
     55 #define USE_FC_COLOR 0
     56 #if OPT_RENDERFONT
     57 #if XftVersion > 20304
     58 #undef USE_FC_COLOR
     59 #define USE_FC_COLOR 1
     60 #endif
     61 #endif
     62 
     63 #define FcOK(func) (func == FcResultMatch)
     64 
     65 #define NoFontWarning(data) (data)->warn = fwAlways
     66 
     67 #define SetFontWidth(screen,dst,src)  (dst)->f_width = (src)
     68 #define SetFontHeight(screen,dst,src) (dst)->f_height = dimRound((double)((screen)->scale_height * (float) (src)))
     69 
     70 /* from X11/Xlibint.h - not all vendors install this file */
     71 #define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
     72 			     (((cs)->rbearing|(cs)->lbearing| \
     73 			       (cs)->ascent|(cs)->descent) == 0))
     74 
     75 #define CI_GET_CHAR_INFO_1D(fs,col,cs) \
     76 { \
     77     cs = NULL; \
     78     if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
     79 	if (fs->per_char == NULL) { \
     80 	    cs = &fs->min_bounds; \
     81 	} else { \
     82 	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
     83 	} \
     84 	if (CI_NONEXISTCHAR(cs)) cs = NULL; \
     85     } \
     86 }
     87 
     88 #define CI_GET_CHAR_INFO_2D(fs,row,col,cs) \
     89 { \
     90     cs = NULL; \
     91     if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
     92 	col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
     93 	if (fs->per_char == NULL) { \
     94 	    cs = &fs->min_bounds; \
     95 	} else { \
     96 	    cs = &fs->per_char[((row - fs->min_byte1) * \
     97 				(fs->max_char_or_byte2 - \
     98 				 fs->min_char_or_byte2 + 1)) + \
     99 			       (col - fs->min_char_or_byte2)]; \
    100 	} \
    101 	if (CI_NONEXISTCHAR(cs)) cs = NULL; \
    102     } \
    103 }
    104 
    105 #define FREE_FNAME(field) \
    106 	    if (fonts == NULL || new_fnames.field != fonts->field) { \
    107 		FREE_STRING(new_fnames.field); \
    108 		new_fnames.field = NULL; \
    109 	    }
    110 
    111 /*
    112  * A structure to hold the relevant properties from a font
    113  * we need to make a well formed font name for it.
    114  */
    115 typedef struct {
    116     /* registry, foundry, family */
    117     const char *beginning;
    118     /* weight */
    119     const char *weight;
    120     /* slant */
    121     const char *slant;
    122     /* wideness */
    123     const char *wideness;
    124     /* add style */
    125     const char *add_style;
    126     int pixel_size;
    127     const char *point_size;
    128     int res_x;
    129     int res_y;
    130     const char *spacing;
    131     int average_width;
    132     /* charset registry, charset encoding */
    133     char *end;
    134 } FontNameProperties;
    135 
    136 #if OPT_WIDE_CHARS && (OPT_RENDERFONT || (OPT_TRACE > 1))
    137 #define MY_UCS(code,high,wide,name) { code, high, wide, #name }
    138 static const struct {
    139     unsigned code, high, wide;
    140     const char *name;
    141 } unicode_boxes[] = {
    142 
    143     MY_UCS(0x2500, 0, 1, box drawings light horizontal),
    144 	MY_UCS(0x2502, 1, 0, box drawings light vertical),
    145 	MY_UCS(0x250c, 2, 2, box drawings light down and right),
    146 	MY_UCS(0x2510, 2, 2, box drawings light down and left),
    147 	MY_UCS(0x2514, 2, 2, box drawings light up and right),
    148 	MY_UCS(0x2518, 2, 2, box drawings light up and left),
    149 	MY_UCS(0x251c, 1, 2, box drawings light vertical and right),
    150 	MY_UCS(0x2524, 1, 2, box drawings light vertical and left),
    151 	MY_UCS(0x252c, 2, 1, box drawings light down and horizontal),
    152 	MY_UCS(0x2534, 2, 1, box drawings light up and horizontal),
    153 	MY_UCS(0x253c, 1, 1, box drawings light vertical and horizontal),
    154     {
    155 	0, 0, 0, NULL
    156     }
    157 };
    158 
    159 #undef MY_UCS
    160 #endif /* OPT_WIDE_CHARS */
    161 
    162 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
    163 static Boolean merge_sublist(char ***, char **);
    164 #endif
    165 
    166 static void save2FontList(XtermWidget, const char *, XtermFontNames *,
    167 			  VTFontEnum, const char *, Bool, Bool);
    168 
    169 #if OPT_RENDERFONT
    170 static void fillInFaceSize(XtermWidget, int);
    171 #endif
    172 
    173 #if OPT_REPORT_FONTS && OPT_TRACE
    174 static void
    175 report_fonts(const char *fmt, ...)
    176 {
    177     va_list ap;
    178     va_start(ap, fmt);
    179     vfprintf(stdout, fmt, ap);
    180     va_end(ap);
    181 #if OPT_TRACE
    182     va_start(ap, fmt);
    183     TraceVA(fmt, ap);
    184     va_end(ap);
    185 #endif
    186 }
    187 
    188 #define ReportFonts report_fonts
    189 #else
    190 #define ReportFonts printf
    191 #endif
    192 
    193 #if OPT_TRACE
    194 static void
    195 set_font_height(TScreen *screen, VTwin *win, int height)
    196 {
    197     SetFontHeight(screen, win, height);
    198     TRACE(("SetFontHeight %d\n", win->f_height));
    199 #undef SetFontHeight
    200 #define SetFontHeight(screen, win, height) set_font_height(screen, win, height)
    201 }
    202 
    203 static void
    204 set_font_width(TScreen *screen, VTwin *win, int width)
    205 {
    206     (void) screen;
    207     SetFontWidth(screen, win, width);
    208     TRACE(("SetFontWidth  %d\n", win->f_width));
    209 #undef  SetFontWidth
    210 #define SetFontWidth(screen, win, width) set_font_width(screen, win, width)
    211 }
    212 #endif
    213 
    214 #if OPT_REPORT_FONTS || OPT_WIDE_CHARS
    215 static unsigned
    216 countGlyphs(XFontStruct *fp)
    217 {
    218     unsigned count = 0;
    219 
    220     if (fp != NULL) {
    221 	if (fp->min_byte1 == 0 && fp->max_byte1 == 0) {
    222 	    count = fp->max_char_or_byte2 - fp->min_char_or_byte2 + 1;
    223 	} else if (fp->min_char_or_byte2 < 256
    224 		   && fp->max_char_or_byte2 < 256) {
    225 	    unsigned first = (fp->min_byte1 << 8) + fp->min_char_or_byte2;
    226 	    unsigned last = (fp->max_byte1 << 8) + fp->max_char_or_byte2;
    227 	    count = last + 1 - first;
    228 	}
    229     }
    230     return count;
    231 }
    232 #endif
    233 
    234 #if OPT_WIDE_CHARS
    235 /*
    236  * Verify that the wide-bold font is at least a bold font with roughly as many
    237  * glyphs as the wide font.  The counts should be the same, but settle for
    238  * filtering out the worst of the font mismatches.
    239  */
    240 static Bool
    241 compatibleWideCounts(XFontStruct *wfs, XFontStruct *wbfs)
    242 {
    243     unsigned count_w = countGlyphs(wfs);
    244     unsigned count_wb = countGlyphs(wbfs);
    245     if (count_w <= 256 ||
    246 	count_wb <= 256 ||
    247 	((count_w / 4) * 3) > count_wb) {
    248 	TRACE(("...font server lied (count wide %u vs wide-bold %u)\n",
    249 	       count_w, count_wb));
    250 	return False;
    251     }
    252     return True;
    253 }
    254 #endif /* OPT_WIDE_CHARS */
    255 
    256 #if OPT_BOX_CHARS
    257 static void
    258 setupPackedFonts(XtermWidget xw)
    259 {
    260     TScreen *screen = TScreenOf(xw);
    261     Bool value = False;
    262 
    263 #if OPT_RENDERFONT
    264     if (xw->work.render_font == True) {
    265 	int e;
    266 
    267 	for (e = 0; e < fMAX; ++e) {
    268 	    XTermXftFonts *data = getMyXftFont(xw, e, screen->menu_font_number);
    269 	    if (data != NULL) {
    270 		if (data->font_info.mixed) {
    271 		    screen->allow_packing = True;
    272 		    break;
    273 		}
    274 	    }
    275 	}
    276     }
    277 #endif /* OPT_RENDERFONT */
    278 
    279     value = screen->allow_packing;
    280 
    281     SetItemSensitivity(fontMenuEntries[fontMenu_font_packedfont].widget, value);
    282 }
    283 #endif
    284 
    285 typedef struct _nameList {
    286     struct _nameList *next;
    287     char *name;
    288 } NameList;
    289 
    290 static NameList *derived_fonts;
    291 
    292 static Boolean
    293 is_derived_font_name(const char *name)
    294 {
    295     Boolean result = False;
    296     NameList *list;
    297     if (!IsEmpty(name)) {
    298 	for (list = derived_fonts; list != NULL; list = list->next) {
    299 	    if (!x_strcasecmp(name, list->name)) {
    300 		result = True;
    301 		break;
    302 	    }
    303 	}
    304     }
    305     return result;
    306 }
    307 
    308 void
    309 xtermDerivedFont(const char *name)
    310 {
    311     if (!IsEmpty(name) && !is_derived_font_name(name)) {
    312 	NameList *list = TypeCalloc(NameList);
    313 	list->name = x_strdup(name);
    314 	list->next = derived_fonts;
    315 	derived_fonts = list;
    316     }
    317 }
    318 
    319 /*
    320  * Returns the fields from start to stop in a dash- separated string.  This
    321  * function will modify the source, putting '\0's in the appropriate place and
    322  * moving the beginning forward to after the '\0'
    323  *
    324  * This will NOT work for the last field (but we won't need it).
    325  */
    326 static char *
    327 n_fields(char **source, int start, int stop)
    328 {
    329     int i;
    330     char *str, *str1;
    331 
    332     /*
    333      * find the start-1th dash
    334      */
    335     for (i = start - 1, str = *source; i; i--, str++) {
    336 	if ((str = strchr(str, '-')) == NULL)
    337 	    return NULL;
    338     }
    339 
    340     /*
    341      * find the stopth dash
    342      */
    343     for (i = stop - start + 1, str1 = str; i; i--, str1++) {
    344 	if ((str1 = strchr(str1, '-')) == NULL)
    345 	    return NULL;
    346     }
    347 
    348     /*
    349      * put a \0 at the end of the fields
    350      */
    351     *(str1 - 1) = '\0';
    352 
    353     /*
    354      * move source forward
    355      */
    356     *source = str1;
    357 
    358     return str;
    359 }
    360 
    361 static Boolean
    362 check_fontname(const char *name)
    363 {
    364     Boolean result = True;
    365 
    366     if (IsEmpty(name)) {
    367 	TRACE(("fontname missing\n"));
    368 	result = False;
    369     }
    370     return result;
    371 }
    372 
    373 /*
    374  * Gets the font properties from a given font structure.  We use the FONT name
    375  * to find them out, since that seems easier.
    376  *
    377  * Returns a pointer to a static FontNameProperties structure
    378  * or NULL on error.
    379  */
    380 static FontNameProperties *
    381 get_font_name_props(Display *dpy, XFontStruct *fs, char **result)
    382 {
    383     static FontNameProperties props;
    384     static char *last_name;
    385 
    386     Atom fontatom;
    387     char *name;
    388     char *str;
    389 
    390     if (fs == NULL)
    391 	return NULL;
    392 
    393     /*
    394      * first get the full font name
    395      */
    396     name = NULL;
    397     fontatom = CachedInternAtom(dpy, "FONT");
    398     if (fontatom != 0) {
    399 	XFontProp *fp;
    400 	int i;
    401 
    402 	for (i = 0, fp = fs->properties; i < fs->n_properties; i++, fp++) {
    403 	    if (fp->name == fontatom) {
    404 		name = XGetAtomName(dpy, fp->card32);
    405 		break;
    406 	    }
    407 	}
    408     }
    409 
    410     if (name == NULL)
    411 	return NULL;
    412 
    413     /*
    414      * XGetAtomName allocates memory - don't leak
    415      */
    416     XFree(last_name);
    417     last_name = name;
    418 
    419     if (result != NULL) {
    420 	if (!check_fontname(name))
    421 	    return NULL;
    422 	free(*result);
    423 	*result = x_strdup(name);
    424     }
    425 
    426     /*
    427      * Now split it up into parts and put them in
    428      * their places. Since we are using parts of
    429      * the original string, we must not free the Atom Name
    430      */
    431 
    432     /* registry, foundry, family */
    433     if ((props.beginning = n_fields(&name, 1, 3)) == NULL)
    434 	return NULL;
    435 
    436     /* weight is the next */
    437     if ((props.weight = n_fields(&name, 1, 1)) == NULL)
    438 	return NULL;
    439 
    440     /* slant */
    441     if ((props.slant = n_fields(&name, 1, 1)) == NULL)
    442 	return NULL;
    443 
    444     /* width */
    445     if ((props.wideness = n_fields(&name, 1, 1)) == NULL)
    446 	return NULL;
    447 
    448     /* add style */
    449     if ((props.add_style = n_fields(&name, 1, 1)) == NULL)
    450 	return NULL;
    451 
    452     /* pixel size */
    453     if ((str = n_fields(&name, 1, 1)) == NULL)
    454 	return NULL;
    455     if ((props.pixel_size = atoi(str)) == 0)
    456 	return NULL;
    457 
    458     /* point size */
    459     if ((props.point_size = n_fields(&name, 1, 1)) == NULL)
    460 	return NULL;
    461 
    462     /* res_x */
    463     if ((str = n_fields(&name, 1, 1)) == NULL)
    464 	return NULL;
    465     if ((props.res_x = atoi(str)) == 0)
    466 	return NULL;
    467 
    468     /* res_y */
    469     if ((str = n_fields(&name, 1, 1)) == NULL)
    470 	return NULL;
    471     if ((props.res_y = atoi(str)) == 0)
    472 	return NULL;
    473 
    474     /* spacing */
    475     if ((props.spacing = n_fields(&name, 1, 1)) == NULL)
    476 	return NULL;
    477 
    478     /* average width */
    479     if ((str = n_fields(&name, 1, 1)) == NULL)
    480 	return NULL;
    481     if ((props.average_width = atoi(str)) == 0)
    482 	return NULL;
    483 
    484     /* the rest: charset registry and charset encoding */
    485     props.end = name;
    486 
    487     return &props;
    488 }
    489 
    490 #define ALLOCHUNK(n) ((n | 127) + 1)
    491 
    492 static void
    493 alloca_fontname(char **result, size_t next)
    494 {
    495     size_t last = (*result != NULL) ? strlen(*result) : 0;
    496     size_t have = (*result != NULL) ? ALLOCHUNK(last) : 0;
    497     size_t want = last + next + 2;
    498 
    499     if (want >= have) {
    500 	char *save = *result;
    501 	want = ALLOCHUNK(want);
    502 	if (last != 0) {
    503 	    *result = TypeRealloc(char, want, *result);
    504 	    if (*result == NULL)
    505 		free(save);
    506 	} else {
    507 	    if ((*result = TypeMallocN(char, want)) != NULL) {
    508 		free(save);
    509 		**result = '\0';
    510 	    }
    511 	}
    512     }
    513 }
    514 
    515 static void
    516 append_fontname_str(char **result, const char *value)
    517 {
    518     if (value == NULL)
    519 	value = "*";
    520     alloca_fontname(result, strlen(value));
    521     if (*result != NULL) {
    522 	if (**result != '\0')
    523 	    strcat(*result, "-");
    524 	strcat(*result, value);
    525     }
    526 }
    527 
    528 static void
    529 append_fontname_num(char **result, int value)
    530 {
    531     if (value < 0) {
    532 	append_fontname_str(result, "*");
    533     } else {
    534 	char temp[100];
    535 	sprintf(temp, "%d", value);
    536 	append_fontname_str(result, temp);
    537     }
    538 }
    539 
    540 /*
    541  * Take the given font props and try to make a well formed font name specifying
    542  * the same base font and size and everything, but with different weight/width
    543  * according to the parameters.  The return value is allocated, should be freed
    544  * by the caller.
    545  */
    546 static char *
    547 derive_font_name(FontNameProperties *props,
    548 		 const char *use_weight,
    549 		 int use_average_width,
    550 		 const char *use_encoding)
    551 {
    552     char *result = NULL;
    553 
    554     append_fontname_str(&result, props->beginning);
    555     append_fontname_str(&result, use_weight);
    556     append_fontname_str(&result, props->slant);
    557     append_fontname_str(&result, NULL);
    558     append_fontname_str(&result, NULL);
    559     append_fontname_num(&result, props->pixel_size);
    560     append_fontname_str(&result, props->point_size);
    561     append_fontname_num(&result, props->res_x);
    562     append_fontname_num(&result, props->res_y);
    563     append_fontname_str(&result, props->spacing);
    564     append_fontname_num(&result, use_average_width);
    565     append_fontname_str(&result, use_encoding);
    566 
    567     xtermDerivedFont(result);
    568     return result;
    569 }
    570 
    571 static char *
    572 bold_font_name(FontNameProperties *props, int use_average_width)
    573 {
    574     return derive_font_name(props, "bold", use_average_width, props->end);
    575 }
    576 
    577 #if OPT_WIDE_ATTRS
    578 static char *
    579 italic_font_name(FontNameProperties *props, const char *slant)
    580 {
    581     FontNameProperties myprops = *props;
    582     myprops.slant = slant;
    583     return derive_font_name(&myprops, props->weight, myprops.average_width, props->end);
    584 }
    585 
    586 static Boolean
    587 open_italic_font(XtermWidget xw, int n, FontNameProperties *fp, XTermFonts * data)
    588 {
    589     static const char *const slant[] =
    590     {
    591 	"o",
    592 	"i"
    593     };
    594     Cardinal pass;
    595     Boolean result = False;
    596 
    597     NoFontWarning(data);
    598     for (pass = 0; pass < XtNumber(slant); ++pass) {
    599 	char *name;
    600 	if ((name = italic_font_name(fp, slant[pass])) != NULL) {
    601 	    TRACE(("open_italic_font %s %s\n",
    602 		   whichFontEnum((VTFontEnum) n), name));
    603 	    if (xtermOpenFont(xw, name, data, NULL, False)) {
    604 		result = (data->fs != NULL);
    605 #if OPT_REPORT_FONTS
    606 		if (resource.reportFonts) {
    607 		    ReportFonts("opened italic version of %s:\n\t%s\n",
    608 				whichFontEnum((VTFontEnum) n),
    609 				name);
    610 		}
    611 #endif
    612 	    }
    613 	    free(name);
    614 	    if (result)
    615 		break;
    616 	}
    617     }
    618 #if OPT_TRACE
    619     if (result) {
    620 	XFontStruct *fs = data->fs;
    621 	if (fs != NULL) {
    622 	    TRACE(("...actual size %dx%d (ascent %d, descent %d)\n",
    623 		   fs->ascent +
    624 		   fs->descent,
    625 		   fs->max_bounds.width,
    626 		   fs->ascent,
    627 		   fs->descent));
    628 	}
    629     }
    630 #endif
    631     return result;
    632 }
    633 #endif
    634 
    635 #if OPT_WIDE_CHARS
    636 #define derive_wide_font(props, weight) \
    637 	derive_font_name(props, weight, props->average_width * 2, "ISO10646-1")
    638 
    639 static char *
    640 wide_font_name(FontNameProperties *props)
    641 {
    642     return derive_wide_font(props, "medium");
    643 }
    644 
    645 static char *
    646 widebold_font_name(FontNameProperties *props)
    647 {
    648     return derive_wide_font(props, "bold");
    649 }
    650 #endif /* OPT_WIDE_CHARS */
    651 
    652 #if OPT_DEC_CHRSET
    653 /*
    654  * Take the given font props and try to make a well formed font name specifying
    655  * the same base font but changed depending on the given attributes and chrset.
    656  *
    657  * For double width fonts, we just double the X-resolution, for double height
    658  * fonts we double the pixel-size and Y-resolution
    659  */
    660 char *
    661 xtermSpecialFont(XTermDraw * params)
    662 {
    663     TScreen *screen = TScreenOf(params->xw);
    664 #if OPT_TRACE
    665     static char old_spacing[80];
    666     static FontNameProperties old_props;
    667 #endif
    668     FontNameProperties *props;
    669     char *result = NULL;
    670     const char *weight;
    671     int pixel_size;
    672     int res_x;
    673     int res_y;
    674 
    675     props = get_font_name_props(screen->display,
    676 				GetNormalFont(screen, fNorm)->fs, NULL);
    677     if (props == NULL)
    678 	return result;
    679 
    680     pixel_size = props->pixel_size;
    681     res_x = props->res_x;
    682     res_y = props->res_y;
    683     if (params->attr_flags & BOLD)
    684 	weight = "bold";
    685     else
    686 	weight = props->weight;
    687 
    688     if (CSET_DOUBLE(params->this_chrset))
    689 	res_x *= 2;
    690 
    691     if (params->this_chrset == CSET_DHL_TOP
    692 	|| params->this_chrset == CSET_DHL_BOT) {
    693 	res_y *= 2;
    694 	pixel_size *= 2;
    695     }
    696 #if OPT_TRACE
    697     if (old_props.res_x != res_x
    698 	|| old_props.res_x != res_y
    699 	|| old_props.pixel_size != pixel_size
    700 	|| strcmp(old_props.spacing, props->spacing)) {
    701 	TRACE(("xtermSpecialFont(atts = %#x, draw = %#x, chrset = %#x)\n",
    702 	       params->attr_flags, params->draw_flags, params->this_chrset));
    703 	TRACE(("res_x      = %d\n", res_x));
    704 	TRACE(("res_y      = %d\n", res_y));
    705 	TRACE(("point_size = %s\n", props->point_size));
    706 	TRACE(("pixel_size = %d\n", pixel_size));
    707 	TRACE(("spacing    = %s\n", props->spacing));
    708 	old_props.res_x = res_x;
    709 	old_props.res_y = res_y;
    710 	old_props.pixel_size = pixel_size;
    711 	old_props.spacing = old_spacing;
    712 	sprintf(old_spacing, "%.*s", (int) sizeof(old_spacing) - 2, props->spacing);
    713     }
    714 #endif
    715 
    716     append_fontname_str(&result, props->beginning);
    717     append_fontname_str(&result, weight);
    718     append_fontname_str(&result, props->slant);
    719     append_fontname_str(&result, props->wideness);
    720     append_fontname_str(&result, props->add_style);
    721     append_fontname_num(&result, pixel_size);
    722     append_fontname_str(&result, props->point_size);
    723     append_fontname_num(&result, (params->draw_flags & NORESOLUTION) ? -1 : res_x);
    724     append_fontname_num(&result, (params->draw_flags & NORESOLUTION) ? -1 : res_y);
    725     append_fontname_str(&result, props->spacing);
    726     append_fontname_str(&result, NULL);
    727     append_fontname_str(&result, props->end);
    728 
    729     xtermDerivedFont(result);
    730     return result;
    731 }
    732 #endif /* OPT_DEC_CHRSET */
    733 
    734 /*
    735  * Case-independent comparison for font-names, including wildcards.
    736  * XLFD allows '?' as a wildcard, but we do not handle that (no one seems
    737  * to use it).
    738  */
    739 static Bool
    740 same_font_name(const char *pattern, const char *match)
    741 {
    742     Bool result = False;
    743 
    744     if (pattern && match) {
    745 	while (*pattern && *match) {
    746 	    if (*pattern == *match) {
    747 		pattern++;
    748 		match++;
    749 	    } else if (*pattern == '*' || *match == '*') {
    750 		if (same_font_name(pattern + 1, match)) {
    751 		    return True;
    752 		} else if (same_font_name(pattern, match + 1)) {
    753 		    return True;
    754 		} else {
    755 		    return False;
    756 		}
    757 	    } else {
    758 		int p = x_toupper(*pattern++);
    759 		int m = x_toupper(*match++);
    760 		if (p != m)
    761 		    return False;
    762 	    }
    763 	}
    764 	result = (*pattern == *match);	/* both should be NUL */
    765     }
    766     return result;
    767 }
    768 
    769 /*
    770  * Double-check the fontname that we asked for versus what the font server
    771  * actually gave us.  The larger fixed fonts do not always have a matching bold
    772  * font, and the font server may try to scale another font or otherwise
    773  * substitute a mismatched font.
    774  *
    775  * If we cannot get what we requested, we will fallback to the original
    776  * behavior, which simulates bold by overstriking each character at one pixel
    777  * offset.
    778  */
    779 static int
    780 got_bold_font(Display *dpy, XFontStruct *fs, String requested)
    781 {
    782     char *actual = NULL;
    783     int got;
    784 
    785     if (get_font_name_props(dpy, fs, &actual) == NULL)
    786 	got = 0;
    787     else
    788 	got = same_font_name(requested, actual);
    789     free(actual);
    790     return got;
    791 }
    792 
    793 /*
    794  * Check normal/bold (or wide/wide-bold) font pairs to see if we will be able
    795  * to check for missing glyphs in a comparable manner.
    796  */
    797 static int
    798 comparable_metrics(XFontStruct *normal, XFontStruct *bold)
    799 {
    800 #define DATA "comparable_metrics: "
    801     int result = 0;
    802 
    803     if (normal == NULL || bold == NULL) {
    804 	;
    805     } else if (normal->all_chars_exist) {
    806 	if (bold->all_chars_exist) {
    807 	    result = 1;
    808 	} else {
    809 	    TRACE((DATA "all chars exist in normal font, but not in bold\n"));
    810 	}
    811     } else if (normal->per_char != NULL) {
    812 	if (bold->per_char != NULL) {
    813 	    result = 1;
    814 	} else {
    815 	    TRACE((DATA "normal font has per-char metrics, but not bold\n"));
    816 	}
    817     } else {
    818 	TRACE((DATA "normal font is not very good!\n"));
    819 	result = 1;		/* give in (we're not going in reverse) */
    820     }
    821     return result;
    822 #undef DATA
    823 }
    824 
    825 /*
    826  * If the font server tries to adjust another font, it may not adjust it
    827  * properly.  Check that the bounding boxes are compatible.  Otherwise we'll
    828  * leave trash on the display when we mix normal and bold fonts.
    829  */
    830 static int
    831 same_font_size(XtermWidget xw, XFontStruct *nfs, XFontStruct *bfs)
    832 {
    833     TScreen *screen = TScreenOf(xw);
    834     int result = 0;
    835 
    836     if (nfs != NULL && bfs != NULL) {
    837 	TRACE(("same_font_size height %d/%d, min %d/%d max %d/%d\n",
    838 	       nfs->ascent + nfs->descent,
    839 	       bfs->ascent + bfs->descent,
    840 	       nfs->min_bounds.width, bfs->min_bounds.width,
    841 	       nfs->max_bounds.width, bfs->max_bounds.width));
    842 	result = screen->free_bold_box
    843 	    || ((nfs->ascent + nfs->descent) == (bfs->ascent + bfs->descent)
    844 		&& (nfs->min_bounds.width == bfs->min_bounds.width
    845 		    || nfs->min_bounds.width == bfs->min_bounds.width + 1)
    846 		&& (nfs->max_bounds.width == bfs->max_bounds.width
    847 		    || nfs->max_bounds.width == bfs->max_bounds.width + 1));
    848     }
    849     return result;
    850 }
    851 
    852 /*
    853  * Check if the font looks like it has fixed width
    854  */
    855 static int
    856 is_fixed_font(XFontStruct *fs)
    857 {
    858     if (fs)
    859 	return (fs->min_bounds.width == fs->max_bounds.width);
    860     return 1;
    861 }
    862 
    863 static int
    864 differing_widths(XFontStruct *a, XFontStruct *b)
    865 {
    866     int result = 0;
    867     if (a != NULL && b != NULL && a->max_bounds.width != b->max_bounds.width)
    868 	result = 1;
    869     return result;
    870 }
    871 
    872 /*
    873  * Check if the font looks like a double width font (i.e. contains
    874  * characters of width X and 2X
    875  */
    876 #if OPT_WIDE_CHARS
    877 static int
    878 is_double_width_font(XFontStruct *fs)
    879 {
    880     return (fs != NULL && ((2 * fs->min_bounds.width) == fs->max_bounds.width));
    881 }
    882 #else
    883 #define is_double_width_font(fs) 0
    884 #endif
    885 
    886 #if OPT_WIDE_CHARS && OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
    887 #define HALF_WIDTH_TEST_STRING "1234567890"
    888 
    889 /* '1234567890' in Chinese characters in UTF-8 */
    890 #define FULL_WIDTH_TEST_STRING "\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89" \
    891                                "\xe5\x9b\x9b\xe4\xba\x94" \
    892 			       "\xef\xa7\x91\xe4\xb8\x83\xe5\x85\xab" \
    893 			       "\xe4\xb9\x9d\xef\xa6\xb2"
    894 
    895 /* '1234567890' in Korean script in UTF-8 */
    896 #define FULL_WIDTH_TEST_STRING2 "\xec\x9d\xbc\xec\x9d\xb4\xec\x82\xbc" \
    897                                 "\xec\x82\xac\xec\x98\xa4" \
    898 			        "\xec\x9c\xa1\xec\xb9\xa0\xed\x8c\x94" \
    899 			        "\xea\xb5\xac\xec\x98\x81"
    900 
    901 #define HALF_WIDTH_CHAR1  0x0031	/* '1' */
    902 #define HALF_WIDTH_CHAR2  0x0057	/* 'W' */
    903 #define FULL_WIDTH_CHAR1  0x4E00	/* CJK Ideograph 'number one' */
    904 #define FULL_WIDTH_CHAR2  0xAC00	/* Korean script syllable 'Ka' */
    905 
    906 static Bool
    907 is_double_width_font_xft(Display *dpy, XftFont *font)
    908 {
    909     XGlyphInfo gi1, gi2;
    910     FcChar32 c1 = HALF_WIDTH_CHAR1, c2 = HALF_WIDTH_CHAR2;
    911     String fwstr = FULL_WIDTH_TEST_STRING;
    912     String hwstr = HALF_WIDTH_TEST_STRING;
    913 
    914     /* Some Korean fonts don't have Chinese characters at all. */
    915     if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR1)) {
    916 	if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR2))
    917 	    return False;	/* Not a CJK font */
    918 	else			/* a Korean font without CJK Ideographs */
    919 	    fwstr = FULL_WIDTH_TEST_STRING2;
    920     }
    921 
    922     XftTextExtents32(dpy, font, &c1, 1, &gi1);
    923     XftTextExtents32(dpy, font, &c2, 1, &gi2);
    924     if (gi1.xOff != gi2.xOff)	/* Not a fixed-width font */
    925 	return False;
    926 
    927     XftTextExtentsUtf8(dpy,
    928 		       font,
    929 		       (_Xconst FcChar8 *) hwstr,
    930 		       (int) strlen(hwstr),
    931 		       &gi1);
    932     XftTextExtentsUtf8(dpy,
    933 		       font,
    934 		       (_Xconst FcChar8 *) fwstr,
    935 		       (int) strlen(fwstr),
    936 		       &gi2);
    937 
    938     /*
    939      * fontconfig and Xft prior to 2.2(?) set the width of half-width
    940      * characters identical to that of full-width character in CJK double-width
    941      * (bi-width / monospace) font even though the former is half as wide as
    942      * the latter.  This was fixed sometime before the release of fontconfig
    943      * 2.2 in early 2003.  See
    944      *  http://bugzilla.mozilla.org/show_bug.cgi?id=196312
    945      * In the meantime, we have to check both possibilities.
    946      */
    947     return ((2 * gi1.xOff == gi2.xOff) || (gi1.xOff == gi2.xOff));
    948 }
    949 #else
    950 #define is_double_width_font_xft(dpy, xftfont) 0
    951 #endif
    952 
    953 #define EmptyFont(fs) (fs != NULL \
    954 		   && ((fs)->ascent + (fs)->descent == 0 \
    955 		    || (fs)->max_bounds.width == 0))
    956 
    957 #define FontSize(fs) (((fs)->ascent + (fs)->descent) \
    958 		    *  (fs)->max_bounds.width)
    959 
    960 const VTFontNames *
    961 xtermFontName(const char *normal)
    962 {
    963     static VTFontNames data;
    964     FREE_STRING(data.f_n);
    965     memset(&data, 0, sizeof(data));
    966     if (normal)
    967 	data.f_n = x_strdup(normal);
    968     return &data;
    969 }
    970 
    971 const VTFontNames *
    972 defaultVTFontNames(XtermWidget xw)
    973 {
    974     static VTFontNames data;
    975     memset(&data, 0, sizeof(data));
    976     data.f_n = DefaultFontN(xw);
    977     data.f_b = DefaultFontB(xw);
    978 #if OPT_WIDE_CHARS
    979     data.f_w = DefaultFontW(xw);
    980     data.f_wb = DefaultFontWB(xw);
    981 #endif
    982     return &data;
    983 }
    984 
    985 static void
    986 cache_menu_font_name(TScreen *screen, int fontnum, int which, const char *name)
    987 {
    988     if (name != NULL) {
    989 	String last = screen->menu_font_names[fontnum][which];
    990 	if (last != NULL) {
    991 	    if (strcmp(last, name)) {
    992 		FREE_STRING(last);
    993 		TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
    994 		screen->menu_font_names[fontnum][which] = x_strdup(name);
    995 	    }
    996 	} else {
    997 	    TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
    998 	    screen->menu_font_names[fontnum][which] = x_strdup(name);
    999 	}
   1000     }
   1001 }
   1002 
   1003 static void
   1004 cannotFont(XtermWidget xw, const char *who, const char *tag, const char *name)
   1005 {
   1006     static NameList *reported;
   1007     NameList *list;
   1008 
   1009     switch (xw->misc.fontWarnings) {
   1010     case fwNever:
   1011 	return;
   1012     case fwResource:
   1013 	if (is_derived_font_name(name))
   1014 	    return;
   1015 	break;
   1016     case fwAlways:
   1017 	break;
   1018     }
   1019     for (list = reported; list != NULL; list = list->next) {
   1020 	if (!x_strcasecmp(name, list->name)) {
   1021 	    return;
   1022 	}
   1023     }
   1024     if ((list = TypeMalloc(NameList)) != NULL) {
   1025 	list->name = x_strdup(name);
   1026 	list->next = reported;
   1027 	reported = list;
   1028     }
   1029     xtermWarning("cannot %s%s%s %sfont \"%s\"\n",
   1030 		 who, *tag ? " " : "", tag,
   1031 		 is_derived_font_name(name) ? "derived " : "",
   1032 		 name);
   1033 }
   1034 
   1035 #if OPT_RENDERFONT
   1036 static void
   1037 noUsableXft(XtermWidget xw, const char *name)
   1038 {
   1039     switch (xw->misc.fontWarnings) {
   1040     case fwNever:
   1041 	return;
   1042     case fwResource:
   1043 	/* these combinations of wide/bold/italic are all "derived" */
   1044 	return;
   1045     case fwAlways:
   1046 	break;
   1047     }
   1048     xtermWarning("did not find a usable %s TrueType font\n", name);
   1049 }
   1050 #endif
   1051 
   1052 XFontStruct *
   1053 xtermLoadQueryFont(XtermWidget xw, const char *name)
   1054 {
   1055     XFontStruct *result = NULL;
   1056     size_t have = strlen(name);
   1057     if (have == 0 || have > MAX_U_STRING) {
   1058 	;			/* just ignore it */
   1059     } else {
   1060 	TScreen *screen = TScreenOf(xw);
   1061 	result = XLoadQueryFont(screen->display, name);
   1062     }
   1063     return result;
   1064 }
   1065 
   1066 /*
   1067  * Open the given font and verify that it is non-empty.  Return false on
   1068  * failure.
   1069  */
   1070 Bool
   1071 xtermOpenFont(XtermWidget xw,
   1072 	      const char *name,
   1073 	      XTermFonts * result,
   1074 	      XTermFonts * current,
   1075 	      Bool force)
   1076 {
   1077     Bool code = False;
   1078 
   1079     TRACE(("xtermOpenFont %d:%d '%s'\n",
   1080 	   result->warn, xw->misc.fontWarnings, NonNull(name)));
   1081 
   1082     if (!IsEmpty(name)) {
   1083 	Bool existing = (current != NULL
   1084 			 && current->fs != NULL
   1085 			 && current->fn != NULL);
   1086 
   1087 	if ((result->fs = xtermLoadQueryFont(xw, name)) != NULL) {
   1088 	    code = True;
   1089 	    if (EmptyFont(result->fs)) {
   1090 		xtermCloseFont(xw, result);
   1091 		code = False;
   1092 	    } else {
   1093 		result->fn = x_strdup(name);
   1094 	    }
   1095 	} else if (XmuCompareISOLatin1(name, DEFFONT) != 0) {
   1096 	    if (result->warn <= xw->misc.fontWarnings
   1097 #if OPT_RENDERFONT
   1098 		&& !UsingRenderFont(xw)
   1099 #endif
   1100 		) {
   1101 		cannotFont(xw, "load", "", name);
   1102 	    } else {
   1103 		TRACE(("xtermOpenFont: cannot load font '%s'\n", name));
   1104 	    }
   1105 	    if (existing) {
   1106 		TRACE(("...continue using font '%s'\n", current->fn));
   1107 		result->fn = x_strdup(current->fn);
   1108 		result->fs = current->fs;
   1109 	    } else if (force) {
   1110 		NoFontWarning(result);
   1111 		code = xtermOpenFont(xw, DEFFONT, result, NULL, True);
   1112 	    }
   1113 	}
   1114     }
   1115     NoFontWarning(result);
   1116     return code;
   1117 }
   1118 
   1119 /*
   1120  * Close the font and free the font info.
   1121  */
   1122 void
   1123 xtermCloseFont(XtermWidget xw, XTermFonts * fnt)
   1124 {
   1125     if (fnt != NULL && fnt->fs != NULL) {
   1126 	TScreen *screen = TScreenOf(xw);
   1127 
   1128 	clrCgsFonts(xw, WhichVWin(screen), fnt);
   1129 	XFreeFont(screen->display, fnt->fs);
   1130 	xtermFreeFontInfo(fnt);
   1131     }
   1132 }
   1133 
   1134 /*
   1135  * Close and free the font (as well as any aliases).
   1136  */
   1137 static void
   1138 xtermCloseFont2(XtermWidget xw, XTermFonts * fnts, int which)
   1139 {
   1140     XFontStruct *thisFont = fnts[which].fs;
   1141 
   1142     if (thisFont != NULL) {
   1143 	int k;
   1144 
   1145 	xtermCloseFont(xw, &fnts[which]);
   1146 	for (k = 0; k < fMAX; ++k) {
   1147 	    if (k != which) {
   1148 		if (thisFont == fnts[k].fs) {
   1149 		    xtermFreeFontInfo(&fnts[k]);
   1150 		}
   1151 	    }
   1152 	}
   1153     }
   1154 }
   1155 
   1156 /*
   1157  * Close the listed fonts, noting that some may use copies of the pointer.
   1158  */
   1159 void
   1160 xtermCloseFonts(XtermWidget xw, XTermFonts * fnts)
   1161 {
   1162     int j;
   1163 
   1164     for (j = 0; j < fMAX; ++j) {
   1165 	xtermCloseFont2(xw, fnts, j);
   1166     }
   1167 }
   1168 
   1169 /*
   1170  * Make a copy of the source, assuming the XFontStruct's to be unique, but
   1171  * ensuring that the names are reallocated to simplify freeing.
   1172  */
   1173 void
   1174 xtermCopyFontInfo(XTermFonts * target, XTermFonts * source)
   1175 {
   1176     xtermFreeFontInfo(target);
   1177     target->chrset = source->chrset;
   1178     target->flags = source->flags;
   1179     target->fn = x_strdup(source->fn);
   1180     target->fs = source->fs;
   1181     target->warn = source->warn;
   1182 }
   1183 
   1184 void
   1185 xtermFreeFontInfo(XTermFonts * target)
   1186 {
   1187     target->chrset = 0;
   1188     target->flags = 0;
   1189     FreeAndNull(target->fn);
   1190     target->fs = NULL;
   1191 }
   1192 
   1193 #if OPT_REPORT_FONTS
   1194 static void
   1195 reportXCharStruct(const char *tag, XCharStruct * cs)
   1196 {
   1197     ReportFonts("\t\t%s:\n", tag);
   1198     ReportFonts("\t\t\tlbearing: %d\n", cs->lbearing);
   1199     ReportFonts("\t\t\trbearing: %d\n", cs->rbearing);
   1200     ReportFonts("\t\t\twidth:    %d\n", cs->width);
   1201     ReportFonts("\t\t\tascent:   %d\n", cs->ascent);
   1202     ReportFonts("\t\t\tdescent:  %d\n", cs->descent);
   1203 }
   1204 
   1205 static void
   1206 fillXCharStruct(XCharStruct * cs, short value)
   1207 {
   1208     cs->lbearing = value;
   1209     cs->rbearing = value;
   1210     cs->width = value;
   1211     cs->ascent = value;
   1212     cs->descent = value;
   1213 }
   1214 
   1215 /* if the per-character data differs from the summary, that is a problem */
   1216 static void
   1217 compareXCharStruct(const char *tag, XCharStruct * actual, XCharStruct * expect)
   1218 {
   1219 #define CompareXCharStruct(field) \
   1220     if (actual->field != expect->field) \
   1221     	ReportFonts("\t\t%s %s differs: %d\n", tag, #field, actual->field)
   1222     CompareXCharStruct(lbearing);
   1223     CompareXCharStruct(rbearing);
   1224     CompareXCharStruct(width);
   1225     CompareXCharStruct(ascent);
   1226     CompareXCharStruct(descent);
   1227 }
   1228 
   1229 static void
   1230 reportXPerChar(XFontStruct *fs)
   1231 {
   1232     XCharStruct *cs = fs->per_char;
   1233 
   1234     if (cs != NULL) {
   1235 	XCharStruct min_bounds;
   1236 	XCharStruct max_bounds;
   1237 	int valid = 0;
   1238 	int total = 0;
   1239 	unsigned first_char = 0;
   1240 	unsigned last_char = 0;
   1241 	unsigned ch;
   1242 
   1243 	if (fs->max_byte1 == 0) {
   1244 	    first_char = fs->min_char_or_byte2;
   1245 	    last_char = fs->max_char_or_byte2;
   1246 	} else {
   1247 	    first_char = (fs->min_byte1 * 256) + fs->min_char_or_byte2;
   1248 	    last_char = (fs->max_byte1 * 256) + fs->max_char_or_byte2;
   1249 	}
   1250 
   1251 	fillXCharStruct(&max_bounds, -32768);
   1252 	fillXCharStruct(&min_bounds, 32767);
   1253 	TRACE2(("\t\tCells: %d..%d\n", first_char, last_char));
   1254 	for (ch = first_char; ch < last_char; ++ch) {
   1255 	    XCharStruct *item = cs + ch - first_char;
   1256 	    ++total;
   1257 	    if (!CI_NONEXISTCHAR(item)) {
   1258 		++valid;
   1259 #define MIN_BOUNDS(field) min_bounds.field = Min(min_bounds.field, item->field)
   1260 		MIN_BOUNDS(lbearing);
   1261 		MIN_BOUNDS(rbearing);
   1262 		MIN_BOUNDS(width);
   1263 		MIN_BOUNDS(ascent);
   1264 		MIN_BOUNDS(descent);
   1265 #define MAX_BOUNDS(field) max_bounds.field = Max(max_bounds.field, item->field)
   1266 		MAX_BOUNDS(lbearing);
   1267 		MAX_BOUNDS(rbearing);
   1268 		MAX_BOUNDS(width);
   1269 		MAX_BOUNDS(ascent);
   1270 		MAX_BOUNDS(descent);
   1271 		TRACE2(("\t\t\t%d: cell [%d .. %d] wide %d high %d / %d\n",
   1272 			ch,
   1273 			item->lbearing,
   1274 			item->rbearing,
   1275 			item->width,
   1276 			item->ascent,
   1277 			item->descent));
   1278 	    } else {
   1279 		TRACE(("\t\t\t%d: cell missing\n", ch));
   1280 	    }
   1281 	}
   1282 	ReportFonts("\t\tPer-character: %d/%d\n", valid, total);
   1283 	compareXCharStruct("Max", &max_bounds, &(fs->max_bounds));
   1284 	compareXCharStruct("Min", &min_bounds, &(fs->min_bounds));
   1285     } else {
   1286 	ReportFonts("\t\tPer-character: none\n");
   1287     }
   1288 }
   1289 
   1290 static void
   1291 reportOneVTFont(const char *tag,
   1292 		XTermFonts * fnt)
   1293 {
   1294     if (!IsEmpty(fnt->fn) && fnt->fs != NULL) {
   1295 	XFontStruct *fs = fnt->fs;
   1296 	unsigned first_char = 0;
   1297 	unsigned last_char = 0;
   1298 
   1299 	if (fs->max_byte1 == 0) {
   1300 	    first_char = fs->min_char_or_byte2;
   1301 	    last_char = fs->max_char_or_byte2;
   1302 	} else {
   1303 	    first_char = (fs->min_byte1 * 256) + fs->min_char_or_byte2;
   1304 	    last_char = (fs->max_byte1 * 256) + fs->max_char_or_byte2;
   1305 	}
   1306 
   1307 	ReportFonts("\t%s: %s\n", tag, NonNull(fnt->fn));
   1308 	ReportFonts("\t\tall chars:     %s\n", (fs->all_chars_exist
   1309 						? "yes"
   1310 						: "no"));
   1311 	ReportFonts("\t\tdefault char:  %u\n", fs->default_char);
   1312 	ReportFonts("\t\tdirection:     %u\n", fs->direction);
   1313 	ReportFonts("\t\tascent:        %d\n", fs->ascent);
   1314 	ReportFonts("\t\tdescent:       %d\n", fs->descent);
   1315 	ReportFonts("\t\tfirst char:    %u\n", first_char);
   1316 	ReportFonts("\t\tlast char:     %u\n", last_char);
   1317 	ReportFonts("\t\tmaximum-chars: %u\n", countGlyphs(fs));
   1318 	if (FontLacksMetrics(fnt)) {
   1319 	    ReportFonts("\t\tmissing-chars: ?\n");
   1320 	    ReportFonts("\t\tpresent-chars: ?\n");
   1321 	} else {
   1322 	    unsigned missing = 0;
   1323 	    unsigned ch;
   1324 	    for (ch = first_char; ch <= last_char; ++ch) {
   1325 		if (xtermMissingChar(ch, fnt)) {
   1326 		    ++missing;
   1327 		}
   1328 	    }
   1329 	    ReportFonts("\t\tmissing-chars: %u\n", missing);
   1330 	    ReportFonts("\t\tpresent-chars: %u\n", countGlyphs(fs) - missing);
   1331 	}
   1332 	ReportFonts("\t\tmin_byte1:     %u\n", fs->min_byte1);
   1333 	ReportFonts("\t\tmax_byte1:     %u\n", fs->max_byte1);
   1334 	ReportFonts("\t\tproperties:    %d\n", fs->n_properties);
   1335 	reportXCharStruct("min_bounds", &(fs->min_bounds));
   1336 	reportXCharStruct("max_bounds", &(fs->max_bounds));
   1337 	reportXPerChar(fs);
   1338 	/* TODO: report fs->properties */
   1339     }
   1340 }
   1341 
   1342 static void
   1343 reportVTFontInfo(XtermWidget xw, int fontnum)
   1344 {
   1345     if (resource.reportFonts) {
   1346 	TScreen *screen = TScreenOf(xw);
   1347 
   1348 	if (fontnum) {
   1349 	    ReportFonts("Loaded VTFonts(font%d)\n", fontnum);
   1350 	} else {
   1351 	    ReportFonts("Loaded VTFonts(default)\n");
   1352 	}
   1353 
   1354 #define ReportOneVTFont(name) reportOneVTFont(#name, screen->fnts + name)
   1355 	ReportOneVTFont(fNorm);
   1356 	ReportOneVTFont(fBold);
   1357 #if OPT_WIDE_CHARS
   1358 	ReportOneVTFont(fWide);
   1359 	ReportOneVTFont(fWBold);
   1360 #endif
   1361     }
   1362 }
   1363 #endif
   1364 
   1365 void
   1366 xtermUpdateFontGCs(XtermWidget xw, MyGetFont myfunc)
   1367 {
   1368     TScreen *screen = TScreenOf(xw);
   1369     VTwin *win = WhichVWin(screen);
   1370     Pixel new_normal = getXtermFG(xw, xw->flags, xw->cur_foreground);
   1371     Pixel new_revers = getXtermBG(xw, xw->flags, xw->cur_background);
   1372 
   1373     setCgsFore(xw, win, gcNorm, new_normal);
   1374     setCgsBack(xw, win, gcNorm, new_revers);
   1375     setCgsFont(xw, win, gcNorm, myfunc(screen, fNorm));
   1376 
   1377     copyCgs(xw, win, gcBold, gcNorm);
   1378     setCgsFont2(xw, win, gcBold, myfunc(screen, fBold), fBold);
   1379 
   1380     setCgsFore(xw, win, gcNormReverse, new_revers);
   1381     setCgsBack(xw, win, gcNormReverse, new_normal);
   1382     setCgsFont(xw, win, gcNormReverse, myfunc(screen, fNorm));
   1383 
   1384     copyCgs(xw, win, gcBoldReverse, gcNormReverse);
   1385     setCgsFont2(xw, win, gcBoldReverse, myfunc(screen, fBold), fBold);
   1386 
   1387     if_OPT_WIDE_CHARS(screen, {
   1388 	XTermFonts *wide_xx = myfunc(screen, fWide);
   1389 	XTermFonts *bold_xx = myfunc(screen, fWBold);
   1390 	if (wide_xx->fs != NULL
   1391 	    && bold_xx->fs != NULL) {
   1392 	    setCgsFore(xw, win, gcWide, new_normal);
   1393 	    setCgsBack(xw, win, gcWide, new_revers);
   1394 	    setCgsFont(xw, win, gcWide, wide_xx);
   1395 
   1396 	    copyCgs(xw, win, gcWBold, gcWide);
   1397 	    setCgsFont(xw, win, gcWBold, bold_xx);
   1398 
   1399 	    setCgsFore(xw, win, gcWideReverse, new_revers);
   1400 	    setCgsBack(xw, win, gcWideReverse, new_normal);
   1401 	    setCgsFont(xw, win, gcWideReverse, wide_xx);
   1402 
   1403 	    copyCgs(xw, win, gcWBoldReverse, gcWideReverse);
   1404 	    setCgsFont(xw, win, gcWBoldReverse, bold_xx);
   1405 	}
   1406     });
   1407 }
   1408 
   1409 #if OPT_WIDE_ATTRS
   1410 unsigned
   1411 xtermUpdateItalics(XtermWidget xw, unsigned new_attrs, unsigned old_attrs)
   1412 {
   1413     TScreen *screen = TScreenOf(xw);
   1414 
   1415     (void) screen;
   1416     if (UseItalicFont(screen)) {
   1417 	if ((new_attrs & ATR_ITALIC) && !(old_attrs & ATR_ITALIC)) {
   1418 	    xtermLoadItalics(xw);
   1419 	    xtermUpdateFontGCs(xw, getItalicFont);
   1420 	} else if (!(new_attrs & ATR_ITALIC) && (old_attrs & ATR_ITALIC)) {
   1421 	    xtermUpdateFontGCs(xw, getNormalFont);
   1422 	}
   1423     }
   1424     return new_attrs;
   1425 }
   1426 #endif
   1427 
   1428 #if OPT_TRACE && OPT_BOX_CHARS
   1429 static void
   1430 show_font_misses(const char *name, XTermFonts * fp)
   1431 {
   1432     if (fp->fs != NULL) {
   1433 	if (FontLacksMetrics(fp)) {
   1434 	    TRACE(("%s font lacks metrics\n", name));
   1435 	} else if (FontIsIncomplete(fp)) {
   1436 	    TRACE(("%s font is incomplete\n", name));
   1437 	} else {
   1438 	    TRACE(("%s font is complete\n", name));
   1439 	}
   1440     } else {
   1441 	TRACE(("%s font is missing\n", name));
   1442     }
   1443 }
   1444 #endif
   1445 
   1446 static Bool
   1447 loadNormFP(XtermWidget xw,
   1448 	   char **nameOutP,
   1449 	   XTermFonts * infoOut,
   1450 	   XTermFonts * current,
   1451 	   int fontnum)
   1452 {
   1453     Bool status = True;
   1454 
   1455     TRACE(("loadNormFP (%s)\n", NonNull(*nameOutP)));
   1456 
   1457     if (!xtermOpenFont(xw,
   1458 		       *nameOutP,
   1459 		       infoOut,
   1460 		       current, (fontnum == fontMenu_default))) {
   1461 	/*
   1462 	 * If we are opening the default font, and it happens to be missing,
   1463 	 * force that to the compiled-in default font, e.g., "fixed".  If we
   1464 	 * cannot open the font, disable it from the menu.
   1465 	 */
   1466 	if (fontnum != fontMenu_fontsel) {
   1467 	    SetItemSensitivity(fontMenuEntries[fontnum].widget, False);
   1468 	}
   1469 	status = False;
   1470     }
   1471     return status;
   1472 }
   1473 
   1474 static Bool
   1475 loadBoldFP(XtermWidget xw,
   1476 	   char **nameOutP,
   1477 	   XTermFonts * infoOut,
   1478 	   const char *nameRef,
   1479 	   XTermFonts * infoRef,
   1480 	   int fontnum)
   1481 {
   1482     TScreen *screen = TScreenOf(xw);
   1483     Bool status = True;
   1484 
   1485     TRACE(("loadBoldFP (%s)\n", NonNull(*nameOutP)));
   1486 
   1487     if (!check_fontname(*nameOutP)) {
   1488 	FontNameProperties *fp;
   1489 	char *normal = x_strdup(nameRef);
   1490 
   1491 	fp = get_font_name_props(screen->display, infoRef->fs, &normal);
   1492 	if (fp != NULL) {
   1493 	    NoFontWarning(infoOut);
   1494 	    *nameOutP = bold_font_name(fp, fp->average_width);
   1495 	    if (!xtermOpenFont(xw, *nameOutP, infoOut, NULL, False)) {
   1496 		free(*nameOutP);
   1497 		*nameOutP = bold_font_name(fp, -1);
   1498 		xtermOpenFont(xw, *nameOutP, infoOut, NULL, False);
   1499 	    }
   1500 	    TRACE(("...derived bold '%s'\n", NonNull(*nameOutP)));
   1501 	}
   1502 	if (fp == NULL || infoOut->fs == NULL) {
   1503 	    xtermCopyFontInfo(infoOut, infoRef);
   1504 	    TRACE(("...cannot load a matching bold font\n"));
   1505 	} else if (comparable_metrics(infoRef->fs, infoOut->fs)
   1506 		   && same_font_size(xw, infoRef->fs, infoOut->fs)
   1507 		   && got_bold_font(screen->display, infoOut->fs, *nameOutP)) {
   1508 	    TRACE(("...got a matching bold font\n"));
   1509 	    cache_menu_font_name(screen, fontnum, fBold, *nameOutP);
   1510 	} else {
   1511 	    xtermCloseFont2(xw, infoOut - fBold, fBold);
   1512 	    *infoOut = *infoRef;
   1513 	    TRACE(("...did not get a matching bold font\n"));
   1514 	}
   1515 	free(normal);
   1516     } else if (!xtermOpenFont(xw, *nameOutP, infoOut, NULL, False)) {
   1517 	xtermCopyFontInfo(infoOut, infoRef);
   1518 	TRACE(("...cannot load bold font '%s'\n", NonNull(*nameOutP)));
   1519     } else {
   1520 	cache_menu_font_name(screen, fontnum, fBold, *nameOutP);
   1521     }
   1522 
   1523     /*
   1524      * Most of the time this call to load the font will succeed, even if
   1525      * there is no wide font :  the X server doubles the width of the
   1526      * normal font, or similar.
   1527      *
   1528      * But if it did fail for some reason, then nevermind.
   1529      */
   1530     if (EmptyFont(infoOut->fs))
   1531 	status = False;		/* can't use a 0-sized font */
   1532 
   1533     if (!same_font_size(xw, infoRef->fs, infoOut->fs)
   1534 	&& (is_fixed_font(infoRef->fs) && is_fixed_font(infoOut->fs))) {
   1535 	TRACE(("...ignoring mismatched normal/bold fonts\n"));
   1536 	xtermCloseFont2(xw, infoOut - fBold, fBold);
   1537 	xtermCopyFontInfo(infoOut, infoRef);
   1538     }
   1539 
   1540     return status;
   1541 }
   1542 
   1543 #if OPT_WIDE_CHARS
   1544 static Bool
   1545 loadWideFP(XtermWidget xw,
   1546 	   char **nameOutP,
   1547 	   XTermFonts * infoOut,
   1548 	   const char *nameRef,
   1549 	   XTermFonts * infoRef,
   1550 	   int fontnum)
   1551 {
   1552     TScreen *screen = TScreenOf(xw);
   1553     Bool status = True;
   1554 
   1555     TRACE(("loadWideFP (%s)\n", NonNull(*nameOutP)));
   1556 
   1557     if (!check_fontname(*nameOutP)
   1558 	&& (screen->utf8_fonts && !is_double_width_font(infoRef->fs))) {
   1559 	char *normal = x_strdup(nameRef);
   1560 	FontNameProperties *fp = get_font_name_props(screen->display,
   1561 						     infoRef->fs, &normal);
   1562 	if (fp != NULL) {
   1563 	    *nameOutP = wide_font_name(fp);
   1564 	    NoFontWarning(infoOut);
   1565 	}
   1566 	free(normal);
   1567     }
   1568 
   1569     if (check_fontname(*nameOutP)) {
   1570 	if (xtermOpenFont(xw, *nameOutP, infoOut, NULL, False)
   1571 	    && is_derived_font_name(*nameOutP)
   1572 	    && EmptyFont(infoOut->fs)) {
   1573 	    xtermCloseFont2(xw, infoOut - fWide, fWide);
   1574 	}
   1575 	if (infoOut->fs == NULL) {
   1576 	    xtermCopyFontInfo(infoOut, infoRef);
   1577 	} else {
   1578 	    TRACE(("...%s wide %s\n",
   1579 		   is_derived_font_name(*nameOutP) ? "derived" : "given",
   1580 		   NonNull(*nameOutP)));
   1581 	    cache_menu_font_name(screen, fontnum, fWide, *nameOutP);
   1582 	}
   1583     } else {
   1584 	xtermCopyFontInfo(infoOut, infoRef);
   1585     }
   1586 #define MinWidthOf(fs) fs->min_bounds.width
   1587 #define MaxWidthOf(fs) fs->max_bounds.width
   1588     xw->work.force_wideFont = False;
   1589     if (MaxWidthOf(infoOut->fs) != (2 * MaxWidthOf(infoRef->fs))) {
   1590 	TRACE(("...reference width %d\n", MaxWidthOf(infoRef->fs)));
   1591 	TRACE(("...?? double-width %d\n", 2 * MaxWidthOf(infoRef->fs)));
   1592 	TRACE(("...actual width    %d\n", MaxWidthOf(infoOut->fs)));
   1593 	xw->work.force_wideFont = True;
   1594     }
   1595     return status;
   1596 }
   1597 
   1598 static Bool
   1599 loadWBoldFP(XtermWidget xw,
   1600 	    char **nameOutP,
   1601 	    XTermFonts * infoOut,
   1602 	    const char *wideNameRef, XTermFonts * wideInfoRef,
   1603 	    const char *boldNameRef, XTermFonts * boldInfoRef,
   1604 	    int fontnum)
   1605 {
   1606     TScreen *screen = TScreenOf(xw);
   1607     Bool status = True;
   1608     char *bold = NULL;
   1609 
   1610     TRACE(("loadWBoldFP (%s)\n", NonNull(*nameOutP)));
   1611 
   1612     if (!check_fontname(*nameOutP)) {
   1613 	FontNameProperties *fp;
   1614 	fp = get_font_name_props(screen->display, boldInfoRef->fs, &bold);
   1615 	if (fp != NULL) {
   1616 	    *nameOutP = widebold_font_name(fp);
   1617 	    NoFontWarning(infoOut);
   1618 	}
   1619     }
   1620 
   1621     if (check_fontname(*nameOutP)) {
   1622 
   1623 	if (xtermOpenFont(xw, *nameOutP, infoOut, NULL, False)
   1624 	    && is_derived_font_name(*nameOutP)
   1625 	    && !compatibleWideCounts(wideInfoRef->fs, infoOut->fs)) {
   1626 	    xtermCloseFont2(xw, infoOut - fWBold, fWBold);
   1627 	}
   1628 
   1629 	if (infoOut->fs == NULL) {
   1630 	    if (is_derived_font_name(*nameOutP))
   1631 		free(*nameOutP);
   1632 	    if (IsEmpty(wideNameRef)) {
   1633 		*nameOutP = x_strdup(boldNameRef);
   1634 		xtermCopyFontInfo(infoOut, boldInfoRef);
   1635 		TRACE(("...cannot load wide-bold, use bold %s\n",
   1636 		       NonNull(boldNameRef)));
   1637 	    } else {
   1638 		*nameOutP = x_strdup(wideNameRef);
   1639 		xtermCopyFontInfo(infoOut, wideInfoRef);
   1640 		TRACE(("...cannot load wide-bold, use wide %s\n",
   1641 		       NonNull(wideNameRef)));
   1642 	    }
   1643 	} else {
   1644 	    TRACE(("...%s wide/bold %s\n",
   1645 		   is_derived_font_name(*nameOutP) ? "derived" : "given",
   1646 		   NonNull(*nameOutP)));
   1647 	    cache_menu_font_name(screen, fontnum, fWBold, *nameOutP);
   1648 	}
   1649     } else if (is_double_width_font(boldInfoRef->fs)) {
   1650 	xtermCopyFontInfo(infoOut, boldInfoRef);
   1651 	TRACE(("...bold font is double-width, use it %s\n", NonNull(boldNameRef)));
   1652     } else {
   1653 	xtermCopyFontInfo(infoOut, wideInfoRef);
   1654 	TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(wideNameRef)));
   1655     }
   1656 
   1657     free(bold);
   1658 
   1659     if (EmptyFont(infoOut->fs)) {
   1660 	status = False;		/* can't use a 0-sized font */
   1661     } else {
   1662 	if ((!comparable_metrics(wideInfoRef->fs, infoOut->fs)
   1663 	     || (!same_font_size(xw, wideInfoRef->fs, infoOut->fs)
   1664 		 && is_fixed_font(wideInfoRef->fs)
   1665 		 && is_fixed_font(infoOut->fs)))) {
   1666 	    TRACE(("...ignoring mismatched normal/bold wide fonts\n"));
   1667 	    xtermCloseFont2(xw, infoOut - fWBold, fWBold);
   1668 	    xtermCopyFontInfo(infoOut, wideInfoRef);
   1669 	}
   1670     }
   1671 
   1672     return status;
   1673 }
   1674 #endif
   1675 
   1676 /*
   1677  * Load a given bitmap font, along with the bold/wide variants.
   1678  * Returns nonzero on success.
   1679  */
   1680 int
   1681 xtermLoadFont(XtermWidget xw,
   1682 	      const VTFontNames * fonts,
   1683 	      Bool doresize,
   1684 	      int fontnum)
   1685 {
   1686     TScreen *screen = TScreenOf(xw);
   1687     VTwin *win = WhichVWin(screen);
   1688 
   1689     VTFontNames new_fnames;
   1690     XTermFonts new_fonts[fMAX];
   1691     XTermFonts old_fonts[fMAX];
   1692     char *tmpname = NULL;
   1693     Boolean proportional = False;
   1694     Boolean recovered;
   1695     int code = 0;
   1696 
   1697     memset(&new_fnames, 0, sizeof(new_fnames));
   1698     memset(new_fonts, 0, sizeof(new_fonts));
   1699     memcpy(&old_fonts, screen->fnts, sizeof(old_fonts));
   1700 
   1701     if (fonts != NULL)
   1702 	new_fnames = *fonts;
   1703     if (!check_fontname(new_fnames.f_n))
   1704 	return code;
   1705 
   1706     if (fontnum == fontMenu_fontescape
   1707 	&& new_fnames.f_n != screen->MenuFontName(fontnum)) {
   1708 	if ((tmpname = x_strdup(new_fnames.f_n)) == NULL)
   1709 	    return code;
   1710     }
   1711 
   1712     TRACE(("Begin Cgs - xtermLoadFont(%s)\n", new_fnames.f_n));
   1713     releaseWindowGCs(xw, win);
   1714 
   1715 #define DbgResource(name, field, index) \
   1716     TRACE(("xtermLoadFont #%d "name" %s%s\n", \
   1717     	   fontnum, \
   1718 	   (new_fonts[index].warn == fwResource) ? "*" : " ", \
   1719 	   NonNull(new_fnames.field)))
   1720     DbgResource("normal", f_n, fNorm);
   1721     DbgResource("bold  ", f_b, fBold);
   1722 #if OPT_WIDE_CHARS
   1723     DbgResource("wide  ", f_w, fWide);
   1724     DbgResource("w/bold", f_wb, fWBold);
   1725 #endif
   1726 
   1727     if (!loadNormFP(xw,
   1728 		    &new_fnames.f_n,
   1729 		    &new_fonts[fNorm],
   1730 		    &old_fonts[fNorm],
   1731 		    fontnum))
   1732 	goto bad;
   1733 
   1734     if (!loadBoldFP(xw,
   1735 		    &new_fnames.f_b,
   1736 		    &new_fonts[fBold],
   1737 		    new_fnames.f_n,
   1738 		    &new_fonts[fNorm],
   1739 		    fontnum))
   1740 	goto bad;
   1741 
   1742     /*
   1743      * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH
   1744      * of normal fonts XLFD, and asking for it.  This plucks out 18x18ja
   1745      * and 12x13ja as the corresponding fonts for 9x18 and 6x13.
   1746      */
   1747     if_OPT_WIDE_CHARS(screen, {
   1748 
   1749 	if (!loadWideFP(xw,
   1750 			&new_fnames.f_w,
   1751 			&new_fonts[fWide],
   1752 			new_fnames.f_n,
   1753 			&new_fonts[fNorm],
   1754 			fontnum))
   1755 	    goto bad;
   1756 
   1757 	if (!loadWBoldFP(xw,
   1758 			 &new_fnames.f_wb,
   1759 			 &new_fonts[fWBold],
   1760 			 new_fnames.f_w,
   1761 			 &new_fonts[fWide],
   1762 			 new_fnames.f_b,
   1763 			 &new_fonts[fBold],
   1764 			 fontnum))
   1765 	    goto bad;
   1766 
   1767     });
   1768 
   1769     /*
   1770      * Normal/bold fonts should be the same width.  Also, the min/max
   1771      * values should be the same.
   1772      */
   1773     if (new_fonts[fNorm].fs != NULL
   1774 	&& new_fonts[fBold].fs != NULL
   1775 	&& (!is_fixed_font(new_fonts[fNorm].fs)
   1776 	    || !is_fixed_font(new_fonts[fBold].fs)
   1777 	    || differing_widths(new_fonts[fNorm].fs, new_fonts[fBold].fs))) {
   1778 	TRACE(("Proportional font! normal %d/%d, bold %d/%d\n",
   1779 	       new_fonts[fNorm].fs->min_bounds.width,
   1780 	       new_fonts[fNorm].fs->max_bounds.width,
   1781 	       new_fonts[fBold].fs->min_bounds.width,
   1782 	       new_fonts[fBold].fs->max_bounds.width));
   1783 	proportional = True;
   1784     }
   1785 
   1786     if_OPT_WIDE_CHARS(screen, {
   1787 	if (new_fonts[fWide].fs != NULL
   1788 	    && new_fonts[fWBold].fs != NULL
   1789 	    && (!is_fixed_font(new_fonts[fWide].fs)
   1790 		|| !is_fixed_font(new_fonts[fWBold].fs)
   1791 		|| differing_widths(new_fonts[fWide].fs, new_fonts[fWBold].fs))) {
   1792 	    TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n",
   1793 		   new_fonts[fWide].fs->min_bounds.width,
   1794 		   new_fonts[fWide].fs->max_bounds.width,
   1795 		   new_fonts[fWBold].fs->min_bounds.width,
   1796 		   new_fonts[fWBold].fs->max_bounds.width));
   1797 	    proportional = True;
   1798 	}
   1799     });
   1800 
   1801     /* TODO : enforce that the width of the wide font is 2* the width
   1802        of the narrow font */
   1803 
   1804     /*
   1805      * If we're switching fonts, free the old ones.  Otherwise we'll leak
   1806      * the memory that is associated with the old fonts.  The
   1807      * XLoadQueryFont call allocates a new XFontStruct.
   1808      */
   1809     xtermCloseFonts(xw, screen->fnts);
   1810 #if OPT_WIDE_ATTRS
   1811     xtermCloseFonts(xw, screen->ifnts);
   1812     screen->ifnts_ok = False;
   1813 #endif
   1814 
   1815     xtermCopyFontInfo(GetNormalFont(screen, fNorm), &new_fonts[fNorm]);
   1816     xtermCopyFontInfo(GetNormalFont(screen, fBold), &new_fonts[fBold]);
   1817 #if OPT_WIDE_CHARS
   1818     xtermCopyFontInfo(GetNormalFont(screen, fWide), &new_fonts[fWide]);
   1819     if (new_fonts[fWBold].fs == NULL)
   1820 	xtermCopyFontInfo(GetNormalFont(screen, fWide), &new_fonts[fWide]);
   1821     xtermCopyFontInfo(GetNormalFont(screen, fWBold), &new_fonts[fWBold]);
   1822 #endif
   1823 
   1824     xtermUpdateFontGCs(xw, getNormalFont);
   1825 
   1826 #if OPT_BOX_CHARS
   1827     screen->allow_packing = proportional;
   1828     setupPackedFonts(xw);
   1829 #endif
   1830     screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed));
   1831     screen->fnt_boxes = 1;
   1832 
   1833 #if OPT_BOX_CHARS
   1834     /*
   1835      * xterm uses character positions 1-31 of a font for the line-drawing
   1836      * characters.  Check that they are all present.  The null character
   1837      * (0) is special, and is not used.
   1838      */
   1839 #if OPT_RENDERFONT
   1840     if (UsingRenderFont(xw)) {
   1841 	/*
   1842 	 * FIXME: we shouldn't even be here if we're using Xft.
   1843 	 */
   1844 	screen->fnt_boxes = 0;
   1845 	TRACE(("assume Xft missing line-drawing chars\n"));
   1846     } else
   1847 #endif
   1848     {
   1849 	unsigned ch;
   1850 
   1851 #if OPT_TRACE
   1852 #define TRACE_MISS(index) show_font_misses(#index, &new_fonts[index])
   1853 	TRACE_MISS(fNorm);
   1854 	TRACE_MISS(fBold);
   1855 #if OPT_WIDE_CHARS
   1856 	TRACE_MISS(fWide);
   1857 	TRACE_MISS(fWBold);
   1858 #endif
   1859 #endif
   1860 
   1861 #if OPT_WIDE_CHARS
   1862 	if (screen->utf8_mode || screen->unicode_font) {
   1863 	    UIntSet(screen->fnt_boxes, 2);
   1864 	    for (ch = 1; ch < 32; ch++) {
   1865 		unsigned n = dec2ucs(screen, ch);
   1866 		if (!is_UCS_SPECIAL(n)
   1867 		    && (n != ch)
   1868 		    && (screen->fnt_boxes & 2)) {
   1869 		    if (xtermMissingChar(n, &new_fonts[fNorm]) ||
   1870 			xtermMissingChar(n, &new_fonts[fBold])) {
   1871 			UIntClr(screen->fnt_boxes, 2);
   1872 			TRACE(("missing graphics character #%d, U+%04X\n",
   1873 			       ch, n));
   1874 			break;
   1875 		    }
   1876 		}
   1877 	    }
   1878 	}
   1879 #endif
   1880 
   1881 	for (ch = 1; ch < 32; ch++) {
   1882 	    if (xtermMissingChar(ch, &new_fonts[fNorm])) {
   1883 		TRACE(("missing normal char #%d\n", ch));
   1884 		UIntClr(screen->fnt_boxes, 1);
   1885 		break;
   1886 	    }
   1887 	    if (xtermMissingChar(ch, &new_fonts[fBold])) {
   1888 		TRACE(("missing bold   char #%d\n", ch));
   1889 		UIntClr(screen->fnt_boxes, 1);
   1890 		break;
   1891 	    }
   1892 	}
   1893 
   1894 	TRACE(("Will %suse internal line-drawing characters (mode %d)\n",
   1895 	       screen->fnt_boxes ? "not " : "",
   1896 	       screen->fnt_boxes));
   1897     }
   1898 #endif
   1899 
   1900     if (screen->always_bold_mode) {
   1901 	screen->enbolden = screen->bold_mode;
   1902     } else {
   1903 	screen->enbolden = screen->bold_mode
   1904 	    && ((new_fonts[fNorm].fs == new_fonts[fBold].fs)
   1905 		|| same_font_name(new_fnames.f_n, new_fnames.f_b));
   1906     }
   1907     TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n",
   1908 	   screen->enbolden ? "" : "not "));
   1909 
   1910     set_menu_font(False);
   1911     screen->menu_font_number = fontnum;
   1912     set_menu_font(True);
   1913     if (tmpname) {		/* if setting escape or sel */
   1914 	if (screen->MenuFontName(fontnum))
   1915 	    FREE_STRING(screen->MenuFontName(fontnum));
   1916 	screen->MenuFontName(fontnum) = tmpname;
   1917 	if (fontnum == fontMenu_fontescape) {
   1918 	    update_font_escape();
   1919 	}
   1920 #if OPT_SHIFT_FONTS
   1921 	screen->menu_font_sizes[fontnum] = FontSize(new_fonts[fNorm].fs);
   1922 #endif
   1923     }
   1924     set_cursor_gcs(xw);
   1925     xtermUpdateFontInfo(xw, doresize);
   1926     TRACE(("Success Cgs - xtermLoadFont\n"));
   1927 #if OPT_REPORT_FONTS
   1928     reportVTFontInfo(xw, fontnum);
   1929 #endif
   1930     FREE_FNAME(f_n);
   1931     FREE_FNAME(f_b);
   1932 #if OPT_WIDE_CHARS
   1933     FREE_FNAME(f_w);
   1934     FREE_FNAME(f_wb);
   1935 #endif
   1936     if (new_fonts[fNorm].fn == new_fonts[fBold].fn) {
   1937 	free(new_fonts[fNorm].fn);
   1938     } else {
   1939 	free(new_fonts[fNorm].fn);
   1940 	free(new_fonts[fBold].fn);
   1941     }
   1942 #if OPT_WIDE_CHARS
   1943     free(new_fonts[fWide].fn);
   1944     free(new_fonts[fWBold].fn);
   1945 #endif
   1946     xtermSetWinSize(xw);
   1947     return 1;
   1948 
   1949   bad:
   1950     recovered = False;
   1951     free(tmpname);
   1952 
   1953 #if OPT_RENDERFONT
   1954     if ((fontnum == fontMenu_fontsel) && (fontnum != screen->menu_font_number)) {
   1955 	int old_fontnum = screen->menu_font_number;
   1956 #if OPT_TOOLBAR
   1957 	SetItemSensitivity(fontMenuEntries[fontnum].widget, True);
   1958 #endif
   1959 	Bell(xw, XkbBI_MinorError, 0);
   1960 	new_fnames.f_n = screen->MenuFontName(old_fontnum);
   1961 	if (xtermLoadFont(xw, &new_fnames, doresize, old_fontnum))
   1962 	    recovered = True;
   1963     } else if (x_strcasecmp(new_fnames.f_n, DEFFONT)
   1964 	       && x_strcasecmp(new_fnames.f_n, old_fonts[fNorm].fn)) {
   1965 	new_fnames.f_n = x_strdup(old_fonts[fNorm].fn);
   1966 	TRACE(("...recovering from failed font-load\n"));
   1967 	if (xtermLoadFont(xw, &new_fnames, doresize, fontnum)) {
   1968 	    recovered = True;
   1969 	    if (fontnum != fontMenu_fontsel) {
   1970 		SetItemSensitivity(fontMenuEntries[fontnum].widget,
   1971 				   UsingRenderFont(xw));
   1972 	    }
   1973 	    TRACE(("...recovered size %dx%d\n",
   1974 		   FontHeight(screen),
   1975 		   FontWidth(screen)));
   1976 	}
   1977     }
   1978 #endif
   1979     if (!recovered) {
   1980 	releaseWindowGCs(xw, win);
   1981 	xtermCloseFonts(xw, new_fonts);
   1982 	TRACE(("Fail Cgs - xtermLoadFont\n"));
   1983 	code = 0;
   1984     }
   1985     return code;
   1986 }
   1987 
   1988 #if OPT_WIDE_ATTRS
   1989 /*
   1990  * (Attempt to) load matching italics for the current normal/bold/etc fonts.
   1991  * If the attempt fails for a given style, use the non-italic font.
   1992  */
   1993 void
   1994 xtermLoadItalics(XtermWidget xw)
   1995 {
   1996     TScreen *screen = TScreenOf(xw);
   1997 
   1998     if (UseItalicFont(screen) && !screen->ifnts_ok) {
   1999 	int n;
   2000 	FontNameProperties *fp;
   2001 	XTermFonts *data;
   2002 
   2003 	screen->ifnts_ok = True;
   2004 	for (n = 0; n < fMAX; ++n) {
   2005 	    switch (n) {
   2006 	    case fNorm:
   2007 		/* FALLTHRU */
   2008 	    case fBold:
   2009 		/* FALLTHRU */
   2010 #if OPT_WIDE_CHARS
   2011 	    case fWide:
   2012 		/* FALLTHRU */
   2013 	    case fWBold:
   2014 #endif
   2015 		/* FALLTHRU */
   2016 		data = getItalicFont(screen, n);
   2017 
   2018 		/*
   2019 		 * FIXME - need to handle font-leaks
   2020 		 */
   2021 		data->fs = NULL;
   2022 		if (getNormalFont(screen, n)->fs != NULL &&
   2023 		    (fp = get_font_name_props(screen->display,
   2024 					      getNormalFont(screen, n)->fs,
   2025 					      NULL)) != NULL) {
   2026 		    if (!open_italic_font(xw, n, fp, data)) {
   2027 			if (n > 0) {
   2028 			    xtermCopyFontInfo(data,
   2029 					      getItalicFont(screen, n - 1));
   2030 			} else {
   2031 			    xtermOpenFont(xw,
   2032 					  getNormalFont(screen, n)->fn,
   2033 					  data, NULL, False);
   2034 			}
   2035 		    }
   2036 		}
   2037 		break;
   2038 	    }
   2039 	}
   2040     }
   2041 }
   2042 #endif
   2043 
   2044 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
   2045 /*
   2046  * Collect font-names that we can modify with the load-vt-fonts() action.
   2047  */
   2048 #define MERGE_SUBFONT(dst,src,name) \
   2049 	if (IsEmpty(dst.name)) { \
   2050 	    TRACE(("MERGE_SUBFONT " #dst "." #name " merge \"%s\"\n", NonNull(src.name))); \
   2051 	    dst.name = x_strdup(src.name); \
   2052 	} else { \
   2053 	    TRACE(("MERGE_SUBFONT " #dst "." #name " found \"%s\"\n", NonNull(dst.name))); \
   2054 	}
   2055 #define MERGE_SUBLIST(dst,src,name) \
   2056 	if (dst.fonts.x11.name == NULL) \
   2057 	    dst.fonts.x11.name = TypeCalloc(char *); \
   2058 	if (merge_sublist(&(dst.fonts.x11.name), src.fonts.x11.name)) { \
   2059 	    TRACE(("MERGE_SUBLIST " #dst "." #name " merge \"%s\"\n", src.fonts.x11.name[0])); \
   2060 	} else { \
   2061 	    TRACE(("MERGE_SUBLIST " #dst "." #name " found \"%s\"\n", dst.fonts.x11.name[0])); \
   2062 	}
   2063 
   2064 #define INFER_SUBFONT(dst,src,name) \
   2065 	if (IsEmpty(dst.name)) { \
   2066 	    TRACE(("INFER_SUBFONT " #dst "." #name " will infer\n")); \
   2067 	    dst.name = x_strdup(""); \
   2068 	} else { \
   2069 	    TRACE(("INFER_SUBFONT " #dst "." #name " found \"%s\"\n", NonNull(dst.name))); \
   2070 	}
   2071 
   2072 #define FREE_MENU_FONTS(dst) \
   2073 	TRACE(("FREE_MENU_FONTS " #dst "\n")); \
   2074 	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
   2075 	    for (m = 0; m < fMAX; ++m) { \
   2076 		FREE_STRING(dst.menu_font_names[n][m]); \
   2077 		dst.menu_font_names[n][m] = NULL; \
   2078 	    } \
   2079 	}
   2080 
   2081 #define COPY_MENU_FONTS(dst,src) \
   2082 	TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \
   2083 	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
   2084 	    for (m = 0; m < fMAX; ++m) { \
   2085 		FREE_STRING(dst.menu_font_names[n][m]); \
   2086 		dst.menu_font_names[n][m] = x_strdup(src.menu_font_names[n][m]); \
   2087 	    } \
   2088 	    TRACE((".. " #dst ".menu_fonts_names[%d] = %s\n", n, NonNull(dst.menu_font_names[n][fNorm]))); \
   2089 	}
   2090 
   2091 #define COPY_DEFAULT_FONTS(target, source) \
   2092 	TRACE(("COPY_DEFAULT_FONTS " #source " to " #target "\n")); \
   2093 	xtermCopyVTFontNames(&target.default_font, &source.default_font)
   2094 
   2095 #define COPY_X11_FONTLISTS(target, source) \
   2096 	TRACE(("COPY_X11_FONTLISTS " #source " to " #target "\n")); \
   2097 	xtermCopyFontLists(xw, &target.fonts.x11, &source.fonts.x11)
   2098 
   2099 static void
   2100 xtermCopyVTFontNames(VTFontNames * target, VTFontNames * source)
   2101 {
   2102 #define COPY_IT(name,field) \
   2103     TRACE((".. "#name" = %s\n", NonNull(source->field))); \
   2104     free(target->field); \
   2105     target->field = x_strdup(source->field)
   2106 
   2107     TRACE(("xtermCopyVTFontNames\n"));
   2108 
   2109     COPY_IT(font, f_n);
   2110     COPY_IT(boldFont, f_b);
   2111 
   2112 #if OPT_WIDE_CHARS
   2113     COPY_IT(wideFont, f_w);
   2114     COPY_IT(wideBoldFont, f_wb);
   2115 #endif
   2116 #undef COPY_IT
   2117 }
   2118 
   2119 static void
   2120 xtermCopyFontLists(XtermWidget xw, VTFontList * target, VTFontList * source)
   2121 {
   2122 #define COPY_IT(name,field) \
   2123     copyFontList(&(target->field), source->field); \
   2124     TRACE_ARGV(".. " #name, source->field)
   2125 
   2126     (void) xw;
   2127     TRACE(("xtermCopyFontLists %s ->%s\n",
   2128 	   whichFontList(xw, source),
   2129 	   whichFontList(xw, target)));
   2130 
   2131     COPY_IT(font, list_n);
   2132     COPY_IT(fontBold, list_b);
   2133 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   2134     COPY_IT(fontItal, list_i);
   2135     COPY_IT(fontBtal, list_bi);
   2136 #endif
   2137 #if OPT_WIDE_CHARS
   2138     COPY_IT(wideFont, list_w);
   2139     COPY_IT(wideBoldFont, list_wb);
   2140     COPY_IT(wideItalFont, list_wi);
   2141     COPY_IT(wideBtalFont, list_wbi);
   2142 #endif
   2143 #undef COPY_IT
   2144 }
   2145 
   2146 void
   2147 xtermSaveVTFonts(XtermWidget xw)
   2148 {
   2149     TScreen *screen = TScreenOf(xw);
   2150     Cardinal n, m;
   2151 
   2152     if (!screen->savedVTFonts) {
   2153 
   2154 	screen->savedVTFonts = True;
   2155 	TRACE(("xtermSaveVTFonts saving original\n"));
   2156 	COPY_DEFAULT_FONTS(screen->cacheVTFonts, xw->misc);
   2157 	COPY_X11_FONTLISTS(screen->cacheVTFonts, xw->work);
   2158 	COPY_MENU_FONTS(screen->cacheVTFonts, xw->screen);
   2159     }
   2160 }
   2161 
   2162 #define SAME_STRING(x,y) ((x) == (y) || ((x) && (y) && !strcmp(x, y)))
   2163 #define SAME_MEMBER(n)   SAME_STRING(a->n, b->n)
   2164 
   2165 static Boolean
   2166 sameSubResources(SubResourceRec * a, SubResourceRec * b)
   2167 {
   2168     Boolean result = True;
   2169 
   2170     if (!SAME_MEMBER(default_font.f_n)
   2171 	|| !SAME_MEMBER(default_font.f_b)
   2172 #if OPT_WIDE_CHARS
   2173 	|| !SAME_MEMBER(default_font.f_w)
   2174 	|| !SAME_MEMBER(default_font.f_wb)
   2175 #endif
   2176 	) {
   2177 	TRACE(("sameSubResources: default_font differs\n"));
   2178 	result = False;
   2179     } else {
   2180 	int n;
   2181 
   2182 	for (n = 0; n < NMENUFONTS; ++n) {
   2183 	    if (!SAME_MEMBER(menu_font_names[n][fNorm])) {
   2184 		TRACE(("sameSubResources: menu_font_names[%d] differs\n", n));
   2185 		result = False;
   2186 		break;
   2187 	    }
   2188 	}
   2189     }
   2190 
   2191     return result;
   2192 }
   2193 
   2194 /*
   2195  * Load the "VT" font names from the given subresource name/class.  These
   2196  * correspond to the VT100 resources.
   2197  */
   2198 static Bool
   2199 xtermLoadVTFonts(XtermWidget xw, String myName, String myClass)
   2200 {
   2201     SubResourceRec subresourceRec;
   2202     SubResourceRec referenceRec;
   2203 
   2204     /*
   2205      * These are duplicates of the VT100 font resources, but with a special
   2206      * application/classname passed in to distinguish them.
   2207      */
   2208     static XtResource font_resources[] =
   2209     {
   2210 	Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT),
   2211 	Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT),
   2212 #if OPT_WIDE_CHARS
   2213 	Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT),
   2214 	Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT),
   2215 #endif
   2216 	Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL),
   2217 	Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL),
   2218 	Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL),
   2219 	Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL),
   2220 	Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL),
   2221 	Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL),
   2222 	Sres(XtNfont7, XtCFont7, MenuFontName(fontMenu_font7), NULL),
   2223     };
   2224     Cardinal n, m;
   2225     Bool status = True;
   2226     TScreen *screen = TScreenOf(xw);
   2227 
   2228     TRACE(("called xtermLoadVTFonts(name=%s, class=%s)\n",
   2229 	   NonNull(myName), NonNull(myClass)));
   2230 
   2231     xtermSaveVTFonts(xw);
   2232 
   2233     if (IsEmpty(myName)) {
   2234 	TRACE(("xtermLoadVTFonts restoring original\n"));
   2235 	COPY_DEFAULT_FONTS(xw->misc, screen->cacheVTFonts);
   2236 	COPY_X11_FONTLISTS(xw->work, screen->cacheVTFonts);
   2237 	FREE_MENU_FONTS(xw->screen);
   2238 	COPY_MENU_FONTS(xw->screen, screen->cacheVTFonts);
   2239     } else {
   2240 	TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass));
   2241 
   2242 	memset(&referenceRec, 0, sizeof(referenceRec));
   2243 	memset(&subresourceRec, 0, sizeof(subresourceRec));
   2244 	XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec,
   2245 			  myName, myClass,
   2246 			  font_resources,
   2247 			  (Cardinal) XtNumber(font_resources),
   2248 			  NULL, (Cardinal) 0);
   2249 
   2250 	/*
   2251 	 * XtGetSubresources returns no status, so we compare the returned
   2252 	 * data against a zero'd struct to see if any data is returned.
   2253 	 */
   2254 	if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec))
   2255 	    && !sameSubResources(&(screen->cacheVTFonts), &subresourceRec)) {
   2256 
   2257 	    screen->mergedVTFonts = True;
   2258 
   2259 	    /*
   2260 	     * To make it simple, reallocate the strings returned by
   2261 	     * XtGetSubresources.  We can free our own strings, but not theirs.
   2262 	     */
   2263 	    ALLOC_STRING(subresourceRec.default_font.f_n);
   2264 	    ALLOC_STRING(subresourceRec.default_font.f_b);
   2265 #if OPT_WIDE_CHARS
   2266 	    ALLOC_STRING(subresourceRec.default_font.f_w);
   2267 	    ALLOC_STRING(subresourceRec.default_font.f_wb);
   2268 #endif
   2269 	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
   2270 		ALLOC_STRING(subresourceRec.MenuFontName(n));
   2271 	    }
   2272 
   2273 	    /*
   2274 	     * Now, save the string to a font-list for consistency
   2275 	     */
   2276 #define ALLOC_SUBLIST(which,field) \
   2277 	    if (subresourceRec.default_font.field != NULL) { \
   2278 		char *blob = x_strdup(subresourceRec.default_font.field); \
   2279 		char *base; \
   2280 		for (base = blob; ; base = NULL) { \
   2281 		    char *item = strtok(base, ","); \
   2282 		    if (item == NULL) \
   2283 			break; \
   2284 		    save2FontList(xw, "cached", \
   2285 				  &(subresourceRec.fonts), \
   2286 				  which, \
   2287 				  item, False, False); \
   2288 		} \
   2289 		free(blob); \
   2290 	    }
   2291 
   2292 	    ALLOC_SUBLIST(fNorm, f_n);
   2293 	    ALLOC_SUBLIST(fBold, f_b);
   2294 #if OPT_WIDE_CHARS
   2295 	    ALLOC_SUBLIST(fWide, f_w);
   2296 	    ALLOC_SUBLIST(fWBold, f_wb);
   2297 #endif
   2298 
   2299 	    /*
   2300 	     * If a particular resource value was not found, use the original.
   2301 	     */
   2302 	    MERGE_SUBFONT(subresourceRec, xw->misc, default_font.f_n);
   2303 	    INFER_SUBFONT(subresourceRec, xw->misc, default_font.f_b);
   2304 	    MERGE_SUBLIST(subresourceRec, xw->work, list_n);
   2305 	    MERGE_SUBLIST(subresourceRec, xw->work, list_b);
   2306 #if OPT_WIDE_CHARS
   2307 	    INFER_SUBFONT(subresourceRec, xw->misc, default_font.f_w);
   2308 	    INFER_SUBFONT(subresourceRec, xw->misc, default_font.f_wb);
   2309 	    MERGE_SUBLIST(subresourceRec, xw->work, list_w);
   2310 	    MERGE_SUBLIST(subresourceRec, xw->work, list_wb);
   2311 #endif
   2312 	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
   2313 		MERGE_SUBFONT(subresourceRec, xw->screen, MenuFontName(n));
   2314 	    }
   2315 
   2316 	    /*
   2317 	     * Finally, copy the subresource data to the widget.
   2318 	     */
   2319 	    COPY_DEFAULT_FONTS(xw->misc, subresourceRec);
   2320 	    COPY_X11_FONTLISTS(xw->work, subresourceRec);
   2321 	    FREE_MENU_FONTS(xw->screen);
   2322 	    COPY_MENU_FONTS(xw->screen, subresourceRec);
   2323 
   2324 	    FREE_STRING(screen->MenuFontName(fontMenu_default));
   2325 	    FREE_STRING(screen->menu_font_names[0][fBold]);
   2326 	    screen->MenuFontName(fontMenu_default) = x_strdup(DefaultFontN(xw));
   2327 	    screen->menu_font_names[0][fBold] = x_strdup(DefaultFontB(xw));
   2328 #if OPT_WIDE_CHARS
   2329 	    FREE_STRING(screen->menu_font_names[0][fWide]);
   2330 	    FREE_STRING(screen->menu_font_names[0][fWBold]);
   2331 	    screen->menu_font_names[0][fWide] = x_strdup(DefaultFontW(xw));
   2332 	    screen->menu_font_names[0][fWBold] = x_strdup(DefaultFontWB(xw));
   2333 #endif
   2334 	    /*
   2335 	     * And remove our copies of strings.
   2336 	     */
   2337 	    FREE_STRING(subresourceRec.default_font.f_n);
   2338 	    FREE_STRING(subresourceRec.default_font.f_b);
   2339 #if OPT_WIDE_CHARS
   2340 	    FREE_STRING(subresourceRec.default_font.f_w);
   2341 	    FREE_STRING(subresourceRec.default_font.f_wb);
   2342 #endif
   2343 	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n) {
   2344 		FREE_STRING(subresourceRec.MenuFontName(n));
   2345 	    }
   2346 	} else {
   2347 	    TRACE(("...no resources found\n"));
   2348 	    status = False;
   2349 	}
   2350     }
   2351     TRACE((".. xtermLoadVTFonts: %d\n", status));
   2352     return status;
   2353 }
   2354 
   2355 #if OPT_WIDE_CHARS
   2356 static Bool
   2357 isWideFont(XFontStruct *fp, const char *tag, Bool nullOk)
   2358 {
   2359     Bool result = False;
   2360 
   2361     (void) tag;
   2362     if (okFont(fp)) {
   2363 	unsigned count = countGlyphs(fp);
   2364 	TRACE(("isWideFont(%s) found %d cells\n", tag, count));
   2365 	result = (count > 256) ? True : False;
   2366     } else {
   2367 	result = nullOk;
   2368     }
   2369     return result;
   2370 }
   2371 
   2372 /*
   2373  * If the current fonts are not wide, load the UTF8 fonts.
   2374  *
   2375  * Called during initialization (for wide-character mode), the fonts have not
   2376  * been setup, so we pass nullOk=True to isWideFont().
   2377  *
   2378  * Called after initialization, e.g., in response to the UTF-8 menu entry
   2379  * (starting from narrow character mode), it checks if the fonts are not wide.
   2380  */
   2381 Bool
   2382 xtermLoadWideFonts(XtermWidget xw, Bool nullOk)
   2383 {
   2384     TScreen *screen = TScreenOf(xw);
   2385     Bool result;
   2386 
   2387     if (EmptyFont(GetNormalFont(screen, fWide)->fs)) {
   2388 	result = (isWideFont(GetNormalFont(screen, fNorm)->fs, "normal", nullOk)
   2389 		  && isWideFont(GetNormalFont(screen, fBold)->fs, "bold", nullOk));
   2390     } else {
   2391 	result = (isWideFont(GetNormalFont(screen, fWide)->fs, "wide", nullOk)
   2392 		  && isWideFont(GetNormalFont(screen, fWBold)->fs,
   2393 				"wide-bold", nullOk));
   2394 	if (result && !screen->utf8_latin1) {
   2395 	    result = (isWideFont(GetNormalFont(screen, fNorm)->fs, "normal", nullOk)
   2396 		      && isWideFont(GetNormalFont(screen, fBold)->fs,
   2397 				    "bold", nullOk));
   2398 	}
   2399     }
   2400     if (!result) {
   2401 	TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : ""));
   2402 	result = xtermLoadVTFonts(xw, XtNutf8Fonts, XtCUtf8Fonts);
   2403     }
   2404     TRACE(("xtermLoadWideFonts:%d\n", result));
   2405     return result;
   2406 }
   2407 #endif /* OPT_WIDE_CHARS */
   2408 
   2409 /*
   2410  * Restore the default fonts, i.e., if we had switched to wide-fonts.
   2411  */
   2412 Bool
   2413 xtermLoadDefaultFonts(XtermWidget xw)
   2414 {
   2415     Bool result;
   2416     result = xtermLoadVTFonts(xw, NULL, NULL);
   2417     TRACE(("xtermLoadDefaultFonts:%d\n", result));
   2418     return result;
   2419 }
   2420 #endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */
   2421 
   2422 #if OPT_LOAD_VTFONTS
   2423 void
   2424 HandleLoadVTFonts(Widget w,
   2425 		  XEvent *event GCC_UNUSED,
   2426 		  String *params,
   2427 		  Cardinal *param_count)
   2428 {
   2429     XtermWidget xw;
   2430 
   2431     if ((xw = getXtermWidget(w)) != NULL) {
   2432 	static char empty[] = "";	/* appease strict compilers */
   2433 
   2434 	TScreen *screen = TScreenOf(xw);
   2435 	char name_buf[80];
   2436 	String name = (String) ((*param_count > 0) ? params[0] : empty);
   2437 	char *myName = MyStackAlloc(strlen(name) + 1, name_buf);
   2438 
   2439 	TRACE(("HandleLoadVTFonts(%d)\n", *param_count));
   2440 	if (myName != NULL) {
   2441 	    char class_buf[80];
   2442 	    String convert = (String) ((*param_count > 1) ? params[1] : myName);
   2443 	    char *myClass = MyStackAlloc(strlen(convert) + 1, class_buf);
   2444 
   2445 	    strcpy(myName, name);
   2446 	    if (myClass != NULL) {
   2447 		strcpy(myClass, convert);
   2448 		if (*param_count == 1)
   2449 		    myClass[0] = x_toupper(myClass[0]);
   2450 
   2451 		if (xtermLoadVTFonts(xw, myName, myClass)) {
   2452 		    int n;
   2453 		    /*
   2454 		     * When switching fonts, try to preserve the font-menu
   2455 		     * selection, since it is less surprising to do that (if
   2456 		     * the font-switching can be undone) than to switch to
   2457 		     * "Default".
   2458 		     */
   2459 		    int font_number = screen->menu_font_number;
   2460 		    if (font_number > fontMenu_lastBuiltin)
   2461 			font_number = fontMenu_lastBuiltin;
   2462 		    for (n = 0; n < NMENUFONTS; ++n) {
   2463 			screen->menu_font_sizes[n] = 0;
   2464 		    }
   2465 		    if (font_number == fontMenu_default) {
   2466 			SetVTFont(xw, font_number, True, defaultVTFontNames(xw));
   2467 		    } else {
   2468 			SetVTFont(xw, font_number, True, NULL);
   2469 		    }
   2470 		}
   2471 		MyStackFree(myClass, class_buf);
   2472 	    }
   2473 	    MyStackFree(myName, name_buf);
   2474 	}
   2475     }
   2476 }
   2477 #endif /* OPT_LOAD_VTFONTS */
   2478 
   2479 /*
   2480  * Set the limits for the box that outlines the cursor.
   2481  */
   2482 void
   2483 xtermSetCursorBox(TScreen *screen)
   2484 {
   2485     static XPoint VTbox[NBOX];
   2486     XPoint *vp;
   2487     int fw = FontWidth(screen) - 1;
   2488     int fh = FontHeight(screen) - 1;
   2489     int ww = isCursorBar(screen) ? fw / 8 : fw;
   2490     int hh = isCursorUnderline(screen) ? fh / 8 : fh;
   2491     if (ww < 2)
   2492 	ww = 2;
   2493     if (hh < 2)
   2494 	hh = 2;
   2495 
   2496     vp = &VTbox[1];
   2497     (vp++)->x = (short) ww;
   2498     (vp++)->y = (short) hh;
   2499     (vp++)->x = (short) -ww;
   2500     vp->y = (short) -hh;
   2501 
   2502     screen->box = VTbox;
   2503 }
   2504 
   2505 #if OPT_RENDERFONT
   2506 
   2507 #define CACHE_XFT(data) if (XftFp(data) != NULL) {\
   2508 	    int err = checkXftWidth(xw, data);\
   2509 	    TRACE(("Xft metrics %s[%d] = %d (%d,%d)%s advance %d, actual %d%s%s\n",\
   2510 		#data,\
   2511 		fontnum,\
   2512 		XftFp(data)->height,\
   2513 		XftFp(data)->ascent,\
   2514 		XftFp(data)->descent,\
   2515 		((XftFp(data)->ascent + XftFp(data)->descent) > XftFp(data)->height ? "*" : ""),\
   2516 		XftFp(data)->max_advance_width,\
   2517 		data->font_info.min_width,\
   2518 		data->font_info.mixed ? " mixed" : "",\
   2519 		err ? " ERROR" : ""));\
   2520 	    if (err) {\
   2521 		xtermCloseXft(screen, data);\
   2522 		memset((data), 0, sizeof(*data));\
   2523 		failed += err;\
   2524 	    }\
   2525 	}
   2526 
   2527 #if OPT_REPORT_FONTS
   2528 static FcChar32
   2529 xtermXftFirstChar(XftFont *xft)
   2530 {
   2531     FcChar32 map[FC_CHARSET_MAP_SIZE];
   2532     FcChar32 next;
   2533     FcChar32 first;
   2534     int i;
   2535 
   2536     first = FcCharSetFirstPage(xft->charset, map, &next);
   2537     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) {
   2538 	if (map[i]) {
   2539 	    FcChar32 bits = map[i];
   2540 	    first += (FcChar32) i *32;
   2541 	    while (!(bits & 0x1)) {
   2542 		bits >>= 1;
   2543 		first++;
   2544 	    }
   2545 	    break;
   2546 	}
   2547     }
   2548     return first;
   2549 }
   2550 
   2551 static FcChar32
   2552 xtermXftLastChar(XftFont *xft)
   2553 {
   2554     FcChar32 temp, last, next;
   2555     FcChar32 map[FC_CHARSET_MAP_SIZE];
   2556     int i;
   2557     last = FcCharSetFirstPage(xft->charset, map, &next);
   2558     while ((temp = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE)
   2559 	last = temp;
   2560     last &= (FcChar32) ~ 0xff;
   2561     for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--) {
   2562 	if (map[i]) {
   2563 	    FcChar32 bits = map[i];
   2564 	    last += (FcChar32) i *32 + 31;
   2565 	    while (!(bits & 0x80000000)) {
   2566 		last--;
   2567 		bits <<= 1;
   2568 	    }
   2569 	    break;
   2570 	}
   2571     }
   2572     return (FcChar32) last;
   2573 }
   2574 #endif /* OPT_REPORT_FONTS */
   2575 
   2576 #if OPT_TRACE
   2577 
   2578 #if !OPT_WIDE_CHARS
   2579 static Char *
   2580 convertToUTF8(Char *buffer, int c)
   2581 {
   2582     buffer[0] = (Char) c;
   2583     buffer[1] = 0;
   2584     return buffer;
   2585 }
   2586 #endif
   2587 
   2588 static void
   2589 dumpXft(XtermWidget xw, XTermXftFonts *data)
   2590 {
   2591     XftFont *xft = XftFp(data);
   2592     TScreen *screen = TScreenOf(xw);
   2593     VTwin *win = WhichVWin(screen);
   2594 
   2595     FcChar32 c;
   2596     FcChar32 first = xtermXftFirstChar(xft);
   2597     FcChar32 last = xtermXftLastChar(xft);
   2598     FcChar32 dump;
   2599     unsigned count = 0;
   2600     unsigned too_high = 0;
   2601     unsigned too_wide = 0;
   2602     Boolean skip = False;
   2603 
   2604     TRACE(("dumpXft " TRACE_L "\n"));
   2605     TRACE(("\tdata range U+%04X..U+%04X\n", first, last));
   2606     TRACE(("\tcode\tcells\tdimensions\n"));
   2607 #if OPT_TRACE < 2
   2608     dump = 255;
   2609 #else
   2610     dump = last;
   2611 #endif
   2612     for (c = first; c <= last; ++c) {
   2613 	if (FcCharSetHasChar(xft->charset, c)) {
   2614 	    int width = CharWidth(screen, c);
   2615 	    XGlyphInfo extents;
   2616 	    Boolean big_x;
   2617 	    Boolean big_y;
   2618 
   2619 	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
   2620 	    big_x = (extents.width > win->f_width);
   2621 	    big_y = (extents.height > win->f_height);
   2622 
   2623 	    if (c <= dump) {
   2624 		Char buffer[80];
   2625 
   2626 		*convertToUTF8(buffer, c) = '\0';
   2627 		TRACE(("%s%s\tU+%04X\t%d\t%.1f x %.1f\t%s\n",
   2628 		       (big_y ? "y" : ""),
   2629 		       (big_x ? "x" : ""),
   2630 		       c, width,
   2631 		       ((double) extents.height) / win->f_height,
   2632 		       ((double) extents.width) / win->f_width,
   2633 		       buffer));
   2634 	    } else if (!skip) {
   2635 		skip = True;
   2636 		TRACE(("\t...skipping\n"));
   2637 	    }
   2638 	    if (big_y)
   2639 		++too_high;
   2640 	    if (big_x)
   2641 		++too_wide;
   2642 	    ++count;
   2643 	}
   2644     }
   2645     TRACE((TRACE_R " %u total, %u too-high, %u too-wide\n", count, too_high, too_wide));
   2646 }
   2647 #define DUMP_XFT(xw, data) dumpXft(xw, data)
   2648 #else
   2649 #define DUMP_XFT(xw, data)	/* nothing */
   2650 #endif
   2651 
   2652 /*
   2653  * Check if this is a FC_COLOR font, which fontconfig misrepresents to "fix" a
   2654  * problem with web browsers.  As of 2018/12 (4 years later), Xft does not work
   2655  * with that.  Even with this workaround, fontconfig has at least one bug which
   2656  * causes it to crash (Debian #917034).
   2657  */
   2658 #ifdef FC_COLOR
   2659 #define GetFcBool(pattern, what) \
   2660     FcOK(FcPatternGetBool(pattern, what, 0, &fcbogus))
   2661 
   2662 static Boolean
   2663 isBogusXft(XftFont *font)
   2664 {
   2665     Boolean result = False;
   2666     if (font != NULL) {
   2667 	FcBool fcbogus;
   2668 	if (GetFcBool(font->pattern, FC_COLOR) && fcbogus) {
   2669 	    TRACE(("...matched color-bitmap font\n"));
   2670 #if !USE_FC_COLOR
   2671 	    result = True;
   2672 #endif
   2673 	} else if (GetFcBool(font->pattern, FC_OUTLINE) && !fcbogus) {
   2674 	    TRACE(("...matched non-outline font\n"));
   2675 	    /* This is legal for regular bitmap fonts - fontconfig attempts to
   2676 	     * find a match - but problematic for misencoded color-bitmap fonts.
   2677 	     */
   2678 	}
   2679     }
   2680     return result;
   2681 }
   2682 #endif
   2683 
   2684 #if OPT_BOX_CHARS
   2685 static void
   2686 setBrokenBoxChars(XtermWidget xw, Bool state)
   2687 {
   2688     TRACE(("setBrokenBoxChars %s\n", BtoS(state)));
   2689     term->work.broken_box_chars = (Boolean) state;
   2690     TScreenOf(xw)->broken_box_chars = (Boolean) state;
   2691     update_font_boxchars();
   2692 }
   2693 
   2694 #else
   2695 #define setBrokenBoxChars(xw, state)	/* nothing */
   2696 #endif
   2697 
   2698 static Boolean
   2699 checkedXftWidth(Display *dpy,
   2700 		XTermXftFonts *source,
   2701 		unsigned limit,
   2702 		Dimension *width,
   2703 		FcChar32 c)
   2704 {
   2705     Boolean result = False;
   2706 
   2707     if (FcCharSetHasChar(XftFp(source)->charset, c)) {
   2708 	XGlyphInfo extents;
   2709 
   2710 	result = True;
   2711 	XftTextExtents32(dpy, XftFp(source), &c, 1, &extents);
   2712 	if (*width < extents.width && extents.width <= limit) {
   2713 	    *width = extents.width;
   2714 	}
   2715     }
   2716     return result;
   2717 }
   2718 
   2719 /*
   2720  * Check if the given character has a glyph known to Xft.  This is likely to be
   2721  * slower than checking our cache.
   2722  *
   2723  * see xc/lib/Xft/xftglyphs.c
   2724  */
   2725 static Bool
   2726 slowXftMissing(XtermWidget xw, XftFont *font, unsigned wc)
   2727 {
   2728     TScreen *screen = TScreenOf(xw);
   2729     Bool result = False;
   2730 
   2731     if (font != NULL) {
   2732 	if (XftCharIndex(screen->display, font, wc) == 0)
   2733 	    result = True;
   2734     }
   2735     return result;
   2736 }
   2737 
   2738 static int
   2739 checkXftWidth(XtermWidget xw, XTermXftFonts *data)
   2740 {
   2741     FcChar32 c;
   2742     FcChar32 last = xtermXftLastChar(XftFp(data));
   2743     Dimension limit = (Dimension) XftFp(data)->max_advance_width;
   2744     Dimension width = 0;
   2745     Dimension width2 = 0;
   2746     int failed = 0;
   2747 #if OPT_WIDE_CHARS
   2748     Cardinal n;
   2749 #endif
   2750 
   2751     data->font_info.min_width = 0;
   2752     data->font_info.max_width = limit;
   2753 
   2754 #if OPT_WIDE_CHARS
   2755     /*
   2756      * Check if the line-drawing characters are all provided in the font.
   2757      * If so, take that into account for the cell-widths.
   2758      */
   2759     for (n = 0; n < XtNumber(unicode_boxes) - 1; ++n) {
   2760 	if (!checkedXftWidth(XtDisplay(xw),
   2761 			     data,
   2762 			     limit,
   2763 			     &width2, unicode_boxes[n].code)) {
   2764 	    width2 = 0;
   2765 	    TRACE(("font omits U+%04X line-drawing symbol\n",
   2766 		   unicode_boxes[n].code));
   2767 	    break;
   2768 	}
   2769     }
   2770 #else
   2771     (void) width2;
   2772 #endif
   2773 
   2774     if (width2 > 0) {
   2775 	Dimension check = (Dimension) (limit + 1) / 2;
   2776 	TRACE(("font provides VT100-style line-drawing\n"));
   2777 	/*
   2778 	 * The "VT100 line-drawing" characters happen to be all "ambiguous
   2779 	 * width" in Unicode's scheme.  That means that they could be twice as
   2780 	 * wide as the Latin-1 characters.
   2781 	 */
   2782 #define FC_ERR(n) (1.2 * (n))
   2783 	if (width2 > FC_ERR(check)) {
   2784 	    TRACE(("line-drawing characters appear to be double-width (ignore)\n"));
   2785 	    setBrokenBoxChars(xw, True);
   2786 	} else if (width2 > width) {
   2787 	    width = width2;
   2788 	}
   2789     } else {
   2790 	TRACE(("font does NOT provide VT100-style line-drawing\n"));
   2791 	setBrokenBoxChars(xw, True);
   2792     }
   2793 
   2794     /*
   2795      * For each printable code, ask what its width is.  Given the maximum width
   2796      * for those, we have a reasonable estimate of the single-column width.
   2797      *
   2798      * Ignore control characters - their extent information is misleading.
   2799      */
   2800     for (c = 32; c < 256; ++c) {
   2801 	if (CharWidth(TScreenOf(xw), c) <= 0)
   2802 	    continue;
   2803 	if (FcCharSetHasChar(XftFp(data)->charset, c)) {
   2804 	    (void) checkedXftWidth(XtDisplay(xw),
   2805 				   data,
   2806 				   data->font_info.max_width,
   2807 				   &width, c);
   2808 	}
   2809     }
   2810 
   2811     /*
   2812      * Sometimes someone uses a symbol font which has no useful ASCII or
   2813      * Latin-1 characters.  Allow that, in case they did it intentionally.
   2814      */
   2815     if (width == 0) {
   2816 	failed = 1;
   2817 	if (last >= 256) {
   2818 	    width = data->font_info.max_width;
   2819 	}
   2820     }
   2821     data->font_info.min_width = width;
   2822     data->font_info.mixed = (data->font_info.max_width >=
   2823 			     (data->font_info.min_width + 1));
   2824     return failed;
   2825 }
   2826 
   2827 #if OPT_TRACE
   2828 static const char *
   2829 nameOfXftFont(XftFont *fp)
   2830 {
   2831     static char *result;
   2832     char buffer[1024];
   2833     FreeAndNull(result);
   2834     if (XftNameUnparse(fp->pattern, buffer, (int) sizeof(buffer))) {
   2835 	char *target;
   2836 	char *source = buffer;
   2837 	if ((target = strtok(source, ":")) != NULL) {
   2838 	    result = x_strdup(target);
   2839 	}
   2840     }
   2841     return NonNull(result);
   2842 }
   2843 #endif
   2844 
   2845 #if OPT_REPORT_FONTS
   2846 static void
   2847 reportXftFonts(XtermWidget xw,
   2848 	       XTermXftFonts *fontData,
   2849 	       int fontNum,
   2850 	       XftFont *fp,
   2851 	       const char *name,
   2852 	       const char *tag,
   2853 	       XftPattern *match)
   2854 {
   2855     if (resource.reportFonts) {
   2856 	char buffer[1024];
   2857 	FcChar32 first_char = xtermXftFirstChar(fp);
   2858 	FcChar32 last_char = xtermXftLastChar(fp);
   2859 	FcChar32 ch;
   2860 	unsigned missing = 0;
   2861 
   2862 	ReportFonts("Loaded XftFonts(%s[%s])\n", name, tag);
   2863 
   2864 	for (ch = first_char; ch <= last_char; ++ch) {
   2865 	    if (xtermXftMissing(xw, fontData, fontNum, fp, ch)) {
   2866 		++missing;
   2867 	    }
   2868 	}
   2869 	ReportFonts("\t\tfirst char:    %u\n", first_char);
   2870 	ReportFonts("\t\tlast char:     %u\n", last_char);
   2871 	ReportFonts("\t\tmissing-chars: %u\n", missing);
   2872 	ReportFonts("\t\tpresent-chars: %u\n", ((last_char - first_char)
   2873 						+ 1 - missing));
   2874 
   2875 	if (XftNameUnparse(match, buffer, (int) sizeof(buffer))) {
   2876 	    char *target;
   2877 	    char *source = buffer;
   2878 	    while ((target = strtok(source, ":")) != NULL) {
   2879 		ReportFonts("\t%s\n", target);
   2880 		source = NULL;
   2881 	    }
   2882 	}
   2883 	fflush(stdout);
   2884     }
   2885 }
   2886 
   2887 static void
   2888 reportXftFallbackFont(XtermWidget xw,
   2889 		      XTermXftFonts *fontData,
   2890 		      int fontNum,
   2891 		      XftFont *font,
   2892 		      XftPattern *match)
   2893 {
   2894     if (resource.reportFonts) {
   2895 	char tag[80];
   2896 	sprintf(tag, "%s#%d",
   2897 		whichXftFonts(xw, fontData),
   2898 		fontNum + 1);
   2899 	reportXftFonts(xw, fontData, fontNum, font, "fallback", tag, match);
   2900     }
   2901 }
   2902 
   2903 #else
   2904 #define reportXftFonts(xw, fontData, fontNum, result, name, tag, match)		/* empty */
   2905 #define reportXftFallbackFont(xw, fontData, fontNum, font, match)	/* empty */
   2906 #endif /* OPT_REPORT_FONTS */
   2907 
   2908 /*
   2909  * Xft discards the pattern-match during open-pattern if the result happens to
   2910  * match a currently-open file, but provides no clue to the caller when it does
   2911  * this.  That is, closing a font-file may leave the data in Xft's cache, while
   2912  * opening a file may free the data used for the match.
   2913  *
   2914  * Because of this problem, we cannot reliably refer to the pattern-match data
   2915  * if it may have been seen before.
   2916  */
   2917 Boolean
   2918 maybeXftCache(XtermWidget xw, XftFont *font)
   2919 {
   2920     Boolean result = False;
   2921     if (font != NULL) {
   2922 	TScreen *screen = TScreenOf(xw);
   2923 	ListXftFonts *p;
   2924 	for (p = screen->list_xft_fonts; p != NULL; p = p->next) {
   2925 	    if (p->font == font) {
   2926 		result = True;
   2927 		break;
   2928 	    }
   2929 	}
   2930 	if (!result) {
   2931 	    p = TypeXtMalloc(ListXftFonts);
   2932 	    if (p != NULL) {
   2933 		p->font = font;
   2934 		p->next = screen->list_xft_fonts;
   2935 		screen->list_xft_fonts = p;
   2936 	    }
   2937 	}
   2938     }
   2939     return result;
   2940 }
   2941 
   2942 /*
   2943  * Drop an entry from the cache, and close the font.
   2944  */
   2945 void
   2946 closeCachedXft(TScreen *screen, XftFont *font)
   2947 {
   2948     if (font != NULL) {
   2949 	ListXftFonts *p, *q;
   2950 
   2951 	for (p = screen->list_xft_fonts, q = NULL; p != NULL; q = p, p = p->next) {
   2952 	    if (p->font == font) {
   2953 		XftFontClose(screen->display, font);
   2954 		if (q != NULL) {
   2955 		    q->next = p->next;
   2956 		} else {
   2957 		    screen->list_xft_fonts = p->next;
   2958 		}
   2959 		free(p);
   2960 		break;
   2961 	    }
   2962 	}
   2963     }
   2964 }
   2965 
   2966 static void
   2967 xtermOpenXft(XtermWidget xw,
   2968 	     XTermXftFonts *fontData,
   2969 	     int fontNum,
   2970 	     const char *name,
   2971 	     XftPattern *pat,
   2972 	     const char *tag)
   2973 {
   2974     TScreen *screen = TScreenOf(xw);
   2975     Display *dpy = screen->display;
   2976     XftResult status;
   2977     XftFont *result = NULL;
   2978 
   2979     TRACE(("xtermOpenXft(name=%s, tag=%s)\n", name, tag));
   2980     if (pat != NULL && (fontNum <= MaxXftCache)) {
   2981 	XftPattern *match;
   2982 
   2983 	FcConfigSubstitute(NULL, pat, FcMatchPattern);
   2984 	XftDefaultSubstitute(dpy, DefaultScreen(dpy), pat);
   2985 
   2986 	match = FcFontMatch(NULL, pat, &status);
   2987 	if (match != NULL) {
   2988 	    Boolean maybeReopened = False;
   2989 	    result = XftFontOpenPattern(dpy, match);
   2990 #ifdef FC_COLOR
   2991 	    if (result != NULL) {
   2992 		if (isBogusXft(result)) {
   2993 		    XftFontClose(dpy, result);
   2994 		    result = NULL;
   2995 		    maybeReopened = True;
   2996 		}
   2997 	    }
   2998 #endif
   2999 	    if (result != NULL) {
   3000 		TRACE(("...matched %s font\n", tag));
   3001 		if (fontData->fs_size < fontNum)
   3002 		    fontData->fs_size = fontNum;
   3003 		XftFpN(fontData, fontNum) = result;
   3004 		XftIsN(fontData, fontNum) = xcOpened;
   3005 		if (!maybeXftCache(xw, result)) {
   3006 		    reportXftFonts(xw, fontData, fontNum, result, name, tag, match);
   3007 		}
   3008 	    } else {
   3009 		TRACE(("...could not open %s font\n", tag));
   3010 		if (!maybeReopened)
   3011 		    XftPatternDestroy(match);
   3012 		if (xw->misc.fontWarnings >= fwAlways) {
   3013 		    cannotFont(xw, "open", tag, name);
   3014 		}
   3015 	    }
   3016 	} else {
   3017 	    TRACE(("...did not match %s font\n", tag));
   3018 	    if (xw->misc.fontWarnings >= fwResource) {
   3019 		cannotFont(xw, "match", tag, name);
   3020 	    }
   3021 	}
   3022     }
   3023     if (result == NULL && (fontNum <= MaxXftCache)) {
   3024 	XftFpN(fontData, fontNum) = NULL;
   3025 	XftIsN(fontData, fontNum) = xcEmpty;
   3026     }
   3027 }
   3028 
   3029 #if OPT_SHIFT_FONTS
   3030 /*
   3031  * Don't make a dependency on the math library for a single function.
   3032  * (Newton Raphson).
   3033  */
   3034 static double
   3035 dimSquareRoot(double value)
   3036 {
   3037     double result = 0.0;
   3038     if (value > 0.0) {
   3039 	int n;
   3040 	double older = value;
   3041 	for (n = 0; n < 10; ++n) {
   3042 	    double delta = (older * older - value) / (2.0 * older);
   3043 	    double newer = older - delta;
   3044 	    older = newer;
   3045 	    result = newer;
   3046 	    if (delta > -0.001 && delta < 0.001)
   3047 		break;
   3048 	}
   3049     }
   3050     return result;
   3051 }
   3052 #endif
   3053 
   3054 #ifdef DEBUG_XFT
   3055 static void
   3056 trace_xft_glyph(XtermWidget xw, XTermXftFonts *data, FT_Face face, int code, const char *name)
   3057 {
   3058     if (xtermXftMissing(xw, data, 0, XftFp(data), code)) {
   3059 	TRACE(("Xft glyph U+%04X missing :%s\n", code, name));
   3060     } else if (FT_Load_Char(face, code, FT_LOAD_RENDER) == 0) {
   3061 	FT_GlyphSlot g = face->glyph;
   3062 	TRACE(("Xft glyph U+%04X size(%3d,%3d) at(%3d,%3d) :%s\n",
   3063 	       code,
   3064 	       g->bitmap.rows, g->bitmap.width,
   3065 	       g->bitmap_top, g->bitmap_left,
   3066 	       name));
   3067     }
   3068 }
   3069 
   3070 #if OPT_WIDE_CHARS
   3071 static void
   3072 trace_xft_line_drawing(XtermWidget xw, XTermXftFonts *data, FT_Face face)
   3073 {
   3074     int n;
   3075     for (n = 0; unicode_boxes[n].code != 0; ++n) {
   3076 	trace_xft_glyph(xw, data, face, unicode_boxes[n].code,
   3077 			unicode_boxes[n].name);
   3078     }
   3079 }
   3080 #else
   3081 #define trace_xft_line_drawing(xw, data, face)	/* nothing */
   3082 #endif
   3083 #endif /* DEBUG_XFT */
   3084 
   3085 /*
   3086  * Check if the line-drawing characters do not fill the bounding box.  If so,
   3087  * they're not useful.
   3088  */
   3089 #if OPT_BOX_CHARS
   3090 static void
   3091 linedrawing_gaps(XtermWidget xw, XTermXftFonts *data)
   3092 {
   3093     Boolean broken;
   3094 
   3095 #if OPT_WIDE_CHARS
   3096     TScreen *screen = TScreenOf(xw);
   3097     int n;
   3098     FT_Face face;
   3099     face = XftLockFace(XftFp(data));
   3100     broken = False;
   3101     for (n = 0; unicode_boxes[n].code; ++n) {
   3102 	unsigned code = unicode_boxes[n].code;
   3103 
   3104 	if (xtermXftMissing(xw, data, 0, XftFp(data), code)) {
   3105 	    TRACE(("Xft glyph U+%04X is missing\n", code));
   3106 	    broken = True;
   3107 	    break;
   3108 	}
   3109 
   3110 	if (FT_Load_Char(face, code, FT_LOAD_RENDER) == 0) {
   3111 	    FT_GlyphSlot g = face->glyph;
   3112 	    TRACE(("Xft glyph U+%04X size(%3d,%3d) at(%3d,%3d) :%s\n",
   3113 		   code,
   3114 		   g->bitmap.rows, g->bitmap.width,
   3115 		   g->bitmap_top, g->bitmap_left,
   3116 		   unicode_boxes[n].name));
   3117 	    /*
   3118 	     * While it is possible for badly-designed fonts to have line
   3119 	     * drawing characters which do not meet, FreeType aggravates the
   3120 	     * situation with its rounding.  Check for an obvious case where
   3121 	     * the weights at the ends of a vertical line do not add up.  That
   3122 	     * shows up as two under-weight rows at the beginning/end of the
   3123 	     * bitmap.
   3124 	     */
   3125 	    if (code == 0x2502) {
   3126 		unsigned r, c;
   3127 		unsigned mids = 0, ends = 0;
   3128 		unsigned char *buffer = g->bitmap.buffer;
   3129 
   3130 		switch (g->bitmap.pixel_mode) {
   3131 		case FT_PIXEL_MODE_MONO:
   3132 		    /* FALLTHRU */
   3133 		case FT_PIXEL_MODE_GRAY:
   3134 		    for (r = 0; r < (unsigned) g->bitmap.rows; ++r) {
   3135 			unsigned k = r * (unsigned) g->bitmap.pitch;
   3136 			unsigned sum = 0;
   3137 			for (c = 0; c < (unsigned) g->bitmap.width; ++c) {
   3138 			    unsigned xx = 0;
   3139 			    switch (g->bitmap.pixel_mode) {
   3140 			    case FT_PIXEL_MODE_MONO:
   3141 				xx = (unsigned) ((buffer[k + (c / 8)]
   3142 						  >> (c % 8)) & 1);
   3143 				break;
   3144 			    case FT_PIXEL_MODE_GRAY:
   3145 				xx = buffer[k + c];
   3146 				break;
   3147 			    }
   3148 			    sum += xx;
   3149 			    TRACE2((" %2x", xx));
   3150 			}
   3151 			TRACE2((" = %u\n", sum));
   3152 			if (r > 0 && (r + 1) < (unsigned) g->bitmap.rows) {
   3153 			    mids = sum;
   3154 			} else {
   3155 			    ends += sum;
   3156 			}
   3157 		    }
   3158 		    TRACE(("...compare middle %u vs ends %u\n", mids, ends));
   3159 		    if ((mids > ends) && (g->bitmap.rows < 16))
   3160 			broken = True;
   3161 		    break;
   3162 		default:
   3163 		    TRACE(("FIXME pixel_mode %d not handled\n",
   3164 			   g->bitmap.pixel_mode));
   3165 		    break;
   3166 		}
   3167 		if (broken)
   3168 		    break;
   3169 	    }
   3170 	    /*
   3171 	     * The factor of two accounts for line-drawing that goes through
   3172 	     * the middle of a cell, possibly leaving half of the cell unused.
   3173 	     * A horizontal line has to extend the full width of the cell.
   3174 	     */
   3175 	    switch (unicode_boxes[n].high) {
   3176 	    case 1:
   3177 		if ((unsigned) g->bitmap.rows < (unsigned) FontHeight(screen)) {
   3178 		    TRACE(("...bitmap is shorter than full-cell (%u vs %u)\n",
   3179 			   (unsigned) g->bitmap.rows,
   3180 			   (unsigned) FontHeight(screen)));
   3181 		    broken = True;
   3182 		}
   3183 		break;
   3184 	    case 2:
   3185 		if ((unsigned) (g->bitmap.rows * 2) < (unsigned) FontHeight(screen)) {
   3186 		    TRACE(("...bitmap is too short for half-cell (%u vs %u)\n",
   3187 			   (unsigned) (g->bitmap.rows * 2),
   3188 			   (unsigned) FontHeight(screen)));
   3189 		    broken = True;
   3190 		}
   3191 		break;
   3192 	    }
   3193 	    switch (unicode_boxes[n].wide) {
   3194 	    case 1:
   3195 		if ((unsigned) g->bitmap.width < (unsigned) FontWidth(screen)) {
   3196 		    TRACE(("...bitmap is narrower than full-cell (%u vs %u)\n",
   3197 			   (unsigned) g->bitmap.width,
   3198 			   (unsigned) FontWidth(screen)));
   3199 		    broken = True;
   3200 		}
   3201 		break;
   3202 	    case 2:
   3203 		if ((unsigned) (g->bitmap.width * 2) < (unsigned) FontWidth(screen)) {
   3204 		    TRACE(("...bitmap is too narrow for half-cell (%u vs %u)\n",
   3205 			   (unsigned) (g->bitmap.width * 2),
   3206 			   (unsigned) FontWidth(screen)));
   3207 		    broken = True;
   3208 		}
   3209 		break;
   3210 	    }
   3211 	    if (broken)
   3212 		break;
   3213 	}
   3214     }
   3215     XftUnlockFace(XftFp(data));
   3216 #else
   3217     (void) data;
   3218     broken = True;
   3219 #endif
   3220 
   3221     if (broken) {
   3222 	TRACE(("Xft line-drawing would not work\n"));
   3223 	setBrokenBoxChars(xw, True);
   3224     }
   3225 }
   3226 #endif /* OPT_BOX_CHARS */
   3227 
   3228 /*
   3229  * Given the Xft font metrics, determine the actual font size.  This is used
   3230  * for each font to ensure that normal, bold and italic fonts follow the same
   3231  * rule.
   3232  */
   3233 static void
   3234 setRenderFontsize(XtermWidget xw, VTwin *win, XTermXftFonts *data, const char *tag)
   3235 {
   3236     XftFont *font = XftFp(data);
   3237     if (font != NULL) {
   3238 	TScreen *screen = TScreenOf(xw);
   3239 	int width, height, ascent, descent;
   3240 #ifdef DEBUG_XFT
   3241 	int n;
   3242 	FT_Face face;
   3243 	FT_Size size;
   3244 	FT_Size_Metrics metrics;
   3245 	Boolean scalable;
   3246 	Boolean is_fixed;
   3247 	Boolean debug_xft = False;
   3248 
   3249 	face = XftLockFace(font);
   3250 	size = face->size;
   3251 	metrics = size->metrics;
   3252 	is_fixed = FT_IS_FIXED_WIDTH(face);
   3253 	scalable = FT_IS_SCALABLE(face);
   3254 	trace_xft_line_drawing(xw, data, face);
   3255 	for (n = 32; n < 127; ++n) {
   3256 	    char name[80];
   3257 	    sprintf(name, "letter \"%c\"", n);
   3258 	    trace_xft_glyph(xw, data, face, n, name);
   3259 	}
   3260 	XftUnlockFace(font);
   3261 
   3262 	/* freetype's inconsistent for this sign */
   3263 	metrics.descender = -metrics.descender;
   3264 
   3265 #define TR_XFT	   "Xft metrics: "
   3266 #define D_64(name) ((double)(metrics.name)/64.0)
   3267 #define M_64(a,b)  ((font->a * 64) != metrics.b)
   3268 #define BOTH(a,b)  D_64(b), M_64(a,b) ? "*" : ""
   3269 
   3270 	debug_xft = (M_64(ascent, ascender)
   3271 		     || M_64(descent, descender)
   3272 		     || M_64(height, height)
   3273 		     || M_64(max_advance_width, max_advance));
   3274 
   3275 	TRACE(("Xft font is %sscalable, %sfixed-width\n",
   3276 	       is_fixed ? "" : "not ",
   3277 	       scalable ? "" : "not "));
   3278 
   3279 	if (debug_xft) {
   3280 	    TRACE(("Xft font size %d+%d vs %d by %d\n",
   3281 		   font->ascent,
   3282 		   font->descent,
   3283 		   font->height,
   3284 		   font->max_advance_width));
   3285 	    TRACE((TR_XFT "ascender    %6.2f%s\n", BOTH(ascent, ascender)));
   3286 	    TRACE((TR_XFT "descender   %6.2f%s\n", BOTH(descent, descender)));
   3287 	    TRACE((TR_XFT "height      %6.2f%s\n", BOTH(height, height)));
   3288 	    TRACE((TR_XFT "max_advance %6.2f%s\n", BOTH(max_advance_width, max_advance)));
   3289 	} else {
   3290 	    TRACE((TR_XFT "matches font\n"));
   3291 	}
   3292 #endif
   3293 
   3294 	width = font->max_advance_width;
   3295 	height = font->height;
   3296 	ascent = font->ascent;
   3297 	descent = font->descent;
   3298 	if (screen->force_xft_height && height < ascent + descent) {
   3299 	    TRACE(("...height is less than ascent + descent (%u vs %u)\n",
   3300 		   height, ascent + descent));
   3301 	    if ((ascent + descent) > (height + 1)) {
   3302 		/* this happens less than 10% of the time */
   3303 		--ascent;
   3304 		--descent;
   3305 		TRACE(("...decrement both ascent and descent before retry\n"));
   3306 	    } else if (ascent > descent) {
   3307 		/* this is the usual case */
   3308 		--ascent;
   3309 		TRACE(("...decrement ascent before retry\n"));
   3310 	    } else {
   3311 		/* this could happen, though rare... */
   3312 		--descent;
   3313 		TRACE(("...decrement descent before retry\n"));
   3314 	    }
   3315 	    height = ascent + descent;
   3316 	    font->ascent = ascent;
   3317 	    font->descent = descent;
   3318 	    TRACE(("...updated height %d vs %d (ascent %d, descent %d)\n",
   3319 		   height, ascent + descent, ascent, descent));
   3320 	}
   3321 	if (is_double_width_font_xft(screen->display, font)) {
   3322 	    TRACE(("...reduce width from %d to %d\n", width, width >> 1));
   3323 	    width >>= 1;
   3324 	}
   3325 	if (tag == NULL) {
   3326 	    SetFontWidth(screen, win, width);
   3327 	    SetFontHeight(screen, win, height);
   3328 	    win->f_ascent = ascent;
   3329 	    win->f_descent = descent;
   3330 	    TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
   3331 		   width, height, ascent, descent));
   3332 	} else if (win->f_width < width ||
   3333 		   win->f_height < height ||
   3334 		   win->f_ascent < ascent ||
   3335 		   win->f_descent < descent) {
   3336 	    TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
   3337 		   tag,
   3338 		   win->f_width, win->f_height, win->f_ascent, win->f_descent,
   3339 		   width, height, ascent, descent));
   3340 
   3341 	    SetFontWidth(screen, win, width);
   3342 	    SetFontHeight(screen, win, height);
   3343 	    win->f_ascent = ascent;
   3344 	    win->f_descent = descent;
   3345 	} else {
   3346 	    TRACE(("setRenderFontsize %s unchanged\n", tag));
   3347 	}
   3348 #if OPT_BOX_CHARS
   3349 	if (!screen->broken_box_chars && (tag == NULL)) {
   3350 	    linedrawing_gaps(xw, data);
   3351 	}
   3352 #endif
   3353     }
   3354 }
   3355 #endif
   3356 
   3357 static void
   3358 checkFontInfo(int value, const char *tag, int failed)
   3359 {
   3360     if (value == 0 || failed) {
   3361 	if (value == 0) {
   3362 	    xtermWarning("Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag);
   3363 	    exit(ERROR_MISC);
   3364 	} else {
   3365 	    xtermWarning("Selected font has no valid %s for ISO-8859-1 encoding\n", tag);
   3366 	}
   3367     }
   3368 }
   3369 
   3370 #if OPT_RENDERFONT
   3371 void
   3372 xtermCloseXft(TScreen *screen, XTermXftFonts *pub)
   3373 {
   3374     if (XftFp(pub) != NULL) {
   3375 	int n;
   3376 
   3377 	if (pub->pattern) {
   3378 	    XftPatternDestroy(pub->pattern);
   3379 	    pub->pattern = NULL;
   3380 	}
   3381 	if (pub->fontset) {
   3382 	    XftFontSetDestroy(pub->fontset);
   3383 	    pub->fontset = NULL;
   3384 	}
   3385 
   3386 	for (n = 0; n <= pub->fs_size; ++n) {
   3387 	    if (XftFpN(pub, n) != NULL) {
   3388 		closeCachedXft(screen, XftFpN(pub, n));
   3389 		XftFpN(pub, n) = NULL;
   3390 		XftIsN(pub, n) = xcEmpty;
   3391 	    }
   3392 	}
   3393 	FreeAndNull(pub->font_map.per_font);
   3394 	memset(pub, 0, sizeof(*pub));
   3395     }
   3396 }
   3397 
   3398 /*
   3399  * Get the faceName/faceNameDoublesize resource setting.
   3400  */
   3401 String
   3402 getFaceName(XtermWidget xw, Bool wideName)
   3403 {
   3404 #if OPT_RENDERWIDE
   3405     String result = (wideName
   3406 		     ? FirstItemOf(xw->work.fonts.xft.list_w)
   3407 		     : CurrentXftFont(xw));
   3408 #else
   3409     String result = CurrentXftFont(xw);
   3410     (void) wideName;
   3411 #endif
   3412     return x_nonempty(result);
   3413 }
   3414 
   3415 /*
   3416  * If we change the faceName, we'll have to re-acquire all of the fonts that
   3417  * are derived from it.
   3418  */
   3419 void
   3420 setFaceName(XtermWidget xw, const char *value)
   3421 {
   3422     TScreen *screen = TScreenOf(xw);
   3423     Boolean changed = (Boolean) ((CurrentXftFont(xw) == NULL)
   3424 				 || strcmp(CurrentXftFont(xw), value));
   3425 
   3426     if (changed) {
   3427 	int n;
   3428 
   3429 	CurrentXftFont(xw) = x_strdup(value);
   3430 	for (n = 0; n < NMENUFONTS; ++n) {
   3431 	    int e;
   3432 	    xw->misc.face_size[n] = -1.0;
   3433 	    for (e = 0; e < fMAX; ++e) {
   3434 		xtermCloseXft(screen, getMyXftFont(xw, e, n));
   3435 	    }
   3436 	}
   3437     }
   3438 }
   3439 #endif
   3440 
   3441 /*
   3442  * Compute useful values for the font/window sizes
   3443  */
   3444 void
   3445 xtermComputeFontInfo(XtermWidget xw,
   3446 		     VTwin *win,
   3447 		     XFontStruct *font,
   3448 		     int sbwidth)
   3449 {
   3450     TScreen *screen = TScreenOf(xw);
   3451 
   3452     int i, j, width, height;
   3453 #if OPT_RENDERFONT
   3454     int fontnum = screen->menu_font_number;
   3455 #endif
   3456     int failed = 0;
   3457 
   3458 #if OPT_RENDERFONT
   3459     /*
   3460      * xterm contains a lot of references to fonts, assuming they are fixed
   3461      * size.  This chunk of code overrides the actual font-selection (see
   3462      * drawXtermText()), if the user has selected render-font.  All of the
   3463      * font-loading for fixed-fonts still goes on whether or not this chunk
   3464      * overrides it.
   3465      */
   3466     if (UsingRenderFont(xw) && fontnum >= 0) {
   3467 	String face_name = getFaceName(xw, False);
   3468 	XTermXftFonts *norm = &(screen->renderFontNorm[fontnum]);
   3469 	XTermXftFonts *bold = &(screen->renderFontBold[fontnum]);
   3470 	XTermXftFonts *ital = &(screen->renderFontItal[fontnum]);
   3471 	XTermXftFonts *btal = &(screen->renderFontBtal[fontnum]);
   3472 #if OPT_RENDERWIDE
   3473 	XTermXftFonts *wnorm = &(screen->renderWideNorm[fontnum]);
   3474 	XTermXftFonts *wbold = &(screen->renderWideBold[fontnum]);
   3475 	XTermXftFonts *wital = &(screen->renderWideItal[fontnum]);
   3476 	XTermXftFonts *wbtal = &(screen->renderWideBtal[fontnum]);
   3477 #endif
   3478 
   3479 	if (XftFp(norm) == NULL && !IsEmpty(face_name)) {
   3480 	    Work *work = &(xw->work);
   3481 	    XftPattern *pat;
   3482 	    double face_size;
   3483 
   3484 	    TRACE(("xtermComputeFontInfo font %d: norm(face %s, size %.1f)\n",
   3485 		   fontnum, face_name,
   3486 		   xw->misc.face_size[fontnum]));
   3487 
   3488 	    TRACE(("Using Xft %d\n", XftGetVersion()));
   3489 	    TRACE(("Using FontConfig %d\n", FC_VERSION));
   3490 
   3491 	    if (work->xft_defaults == NULL) {
   3492 		FcInit();
   3493 		work->xft_defaults = FcPatternCreate();
   3494 		XftDefaultSubstitute(screen->display,
   3495 				     XScreenNumberOfScreen(XtScreen(xw)),
   3496 				     work->xft_defaults);
   3497 		if (screen->xft_max_glyph_memory > 0) {
   3498 		    FcPatternAddInteger(work->xft_defaults,
   3499 					XFT_MAX_GLYPH_MEMORY,
   3500 					screen->xft_max_glyph_memory);
   3501 		}
   3502 		if (screen->xft_max_unref_fonts > 0) {
   3503 		    FcPatternAddInteger(work->xft_defaults,
   3504 					XFT_MAX_UNREF_FONTS,
   3505 					screen->xft_max_unref_fonts);
   3506 		}
   3507 #ifdef XFT_TRACK_MEM_USAGE
   3508 		FcPatternAddBool(work->xft_defaults,
   3509 				 XFT_TRACK_MEM_USAGE,
   3510 				 screen->xft_track_mem_usage);
   3511 #endif
   3512 		XftDefaultSet(screen->display, work->xft_defaults);
   3513 	    }
   3514 
   3515 	    fillInFaceSize(xw, fontnum);
   3516 	    face_size = (double) xw->misc.face_size[fontnum];
   3517 
   3518 	    /*
   3519 	     * By observation (there is no documentation), XftPatternBuild is
   3520 	     * cumulative.  Build the bold- and italic-patterns on top of the
   3521 	     * normal pattern.
   3522 	     */
   3523 #ifdef FC_COLOR
   3524 #if USE_FC_COLOR
   3525 #define NormXftPattern \
   3526 	    XFT_FAMILY,     XftTypeString, "mono", \
   3527 	    FC_OUTLINE,     XftTypeBool,   FcTrue, \
   3528 	    XFT_SIZE,       XftTypeDouble, face_size
   3529 #else
   3530 #define NormXftPattern \
   3531 	    XFT_FAMILY,     XftTypeString, "mono", \
   3532 	    FC_COLOR,       XftTypeBool,   FcFalse, \
   3533 	    FC_OUTLINE,     XftTypeBool,   FcTrue, \
   3534 	    XFT_SIZE,       XftTypeDouble, face_size
   3535 #endif
   3536 #else
   3537 #define NormXftPattern \
   3538 	    XFT_FAMILY,     XftTypeString, "mono", \
   3539 	    XFT_SIZE,       XftTypeDouble, face_size
   3540 #endif
   3541 
   3542 #define BoldXftPattern(norm) \
   3543 	    XFT_WEIGHT,     XftTypeInteger, XFT_WEIGHT_BOLD, \
   3544 	    XFT_CHAR_WIDTH, XftTypeInteger, XftFp(norm)->max_advance_width
   3545 
   3546 #define ItalXftPattern(norm) \
   3547 	    XFT_SLANT,      XftTypeInteger, XFT_SLANT_ITALIC, \
   3548 	    XFT_CHAR_WIDTH, XftTypeInteger, XftFp(norm)->max_advance_width
   3549 
   3550 #define BtalXftPattern(norm) \
   3551 	    XFT_WEIGHT,     XftTypeInteger, XFT_WEIGHT_BOLD, \
   3552 	    XFT_SLANT,      XftTypeInteger, XFT_SLANT_ITALIC, \
   3553 	    XFT_CHAR_WIDTH, XftTypeInteger, XftFp(norm)->max_advance_width
   3554 
   3555 #if OPT_WIDE_ATTRS
   3556 #define HAVE_ITALICS 1
   3557 #define FIND_ITALICS ((pat = XftNameParse(face_name)) != NULL)
   3558 #elif OPT_ISO_COLORS
   3559 #define HAVE_ITALICS 1
   3560 #define FIND_ITALICS (screen->italicULMode && (pat = XftNameParse(face_name)) != 0)
   3561 #else
   3562 #define HAVE_ITALICS 0
   3563 #endif
   3564 
   3565 #if OPT_DEC_CHRSET
   3566 	    freeall_DoubleFT(xw);
   3567 #endif
   3568 	    if ((pat = XftNameParse(face_name)) != NULL) {
   3569 #define OPEN_XFT(data, tag) xtermOpenXft(xw, data, 0, face_name, data->pattern, tag)
   3570 		norm->pattern = XftPatternDuplicate(pat);
   3571 		XftPatternBuild(norm->pattern,
   3572 				NormXftPattern,
   3573 				(void *) 0);
   3574 		OPEN_XFT(norm, "normal");
   3575 
   3576 		if (XftFp(norm) != NULL) {
   3577 		    bold->pattern = XftPatternDuplicate(pat);
   3578 		    XftPatternBuild(bold->pattern,
   3579 				    NormXftPattern,
   3580 				    BoldXftPattern(norm),
   3581 				    (void *) 0);
   3582 		    OPEN_XFT(bold, "bold");
   3583 
   3584 #if HAVE_ITALICS
   3585 		    if (FIND_ITALICS) {
   3586 			ital->pattern = XftPatternDuplicate(pat);
   3587 			XftPatternBuild(ital->pattern,
   3588 					NormXftPattern,
   3589 					ItalXftPattern(norm),
   3590 					(void *) 0);
   3591 			OPEN_XFT(ital, "italic");
   3592 			btal->pattern = XftPatternDuplicate(pat);
   3593 			XftPatternBuild(btal->pattern,
   3594 					NormXftPattern,
   3595 					BtalXftPattern(norm),
   3596 					(void *) 0);
   3597 			OPEN_XFT(btal, "bold-italic");
   3598 		    }
   3599 #endif
   3600 
   3601 		    /*
   3602 		     * FIXME:  just assume that the corresponding font has no
   3603 		     * graphics characters.
   3604 		     */
   3605 		    if (screen->fnt_boxes) {
   3606 			screen->fnt_boxes = 0;
   3607 			TRACE(("Xft opened - will not use internal line-drawing characters\n"));
   3608 		    }
   3609 		}
   3610 
   3611 		CACHE_XFT(norm);
   3612 
   3613 		CACHE_XFT(bold);
   3614 		if (XftFp(norm) != NULL && !XftFp(bold)) {
   3615 		    noUsableXft(xw, "bold");
   3616 		    XftPatternDestroy(bold->pattern);
   3617 		    bold->pattern = XftPatternDuplicate(pat);
   3618 		    XftPatternBuild(bold->pattern,
   3619 				    NormXftPattern,
   3620 				    (void *) 0);
   3621 		    OPEN_XFT(bold, "bold");
   3622 		    failed = 0;
   3623 		    CACHE_XFT(bold);
   3624 		}
   3625 #if HAVE_ITALICS
   3626 		CACHE_XFT(ital);
   3627 		if (XftFp(norm) != NULL && !XftFp(ital)) {
   3628 		    noUsableXft(xw, "italic");
   3629 		    XftPatternDestroy(ital->pattern);
   3630 		    ital->pattern = XftPatternDuplicate(pat);
   3631 		    XftPatternBuild(ital->pattern,
   3632 				    NormXftPattern,
   3633 				    (void *) 0);
   3634 		    OPEN_XFT(ital, "italics");
   3635 		    failed = 0;
   3636 		    CACHE_XFT(ital);
   3637 		}
   3638 		CACHE_XFT(btal);
   3639 		if (XftFp(norm) != NULL && !XftFp(btal)) {
   3640 		    noUsableXft(xw, "bold italic");
   3641 		    XftPatternDestroy(btal->pattern);
   3642 		    btal->pattern = XftPatternDuplicate(pat);
   3643 		    XftPatternBuild(btal->pattern,
   3644 				    NormXftPattern,
   3645 				    (void *) 0);
   3646 		    OPEN_XFT(btal, "bold-italics");
   3647 		    failed = 0;
   3648 		    CACHE_XFT(btal);
   3649 		}
   3650 #endif
   3651 		XftPatternDestroy(pat);
   3652 	    } else {
   3653 		failed = 1;
   3654 	    }
   3655 
   3656 	    /*
   3657 	     * See xtermXftDrawString().  A separate double-width font is nice
   3658 	     * to have, but not essential.
   3659 	     */
   3660 #if OPT_RENDERWIDE
   3661 	    if (XftFp(norm) != NULL && screen->wide_chars) {
   3662 		int char_width = XftFp(norm)->max_advance_width * 2;
   3663 		double aspect = ((FirstItemOf(xw->work.fonts.xft.list_w)
   3664 				  || screen->renderFontNorm[fontnum].font_info.mixed)
   3665 				 ? 1.0
   3666 				 : 2.0);
   3667 
   3668 		face_name = getFaceName(xw, True);
   3669 		TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
   3670 		       NonNull(face_name),
   3671 		       char_width));
   3672 
   3673 #define WideXftPattern \
   3674 		XFT_FAMILY,     XftTypeString,   "mono", \
   3675 		XFT_SIZE,       XftTypeDouble,   face_size, \
   3676 		XFT_SPACING,    XftTypeInteger,  XFT_MONO, \
   3677 		XFT_CHAR_WIDTH, XftTypeInteger,  char_width, \
   3678 		FC_ASPECT,      XftTypeDouble,   aspect
   3679 
   3680 		if (!IsEmpty(face_name) && (pat = XftNameParse(face_name))
   3681 		    != NULL) {
   3682 		    wnorm->pattern = XftPatternDuplicate(pat);
   3683 		    XftPatternBuild(wnorm->pattern,
   3684 				    WideXftPattern,
   3685 				    (void *) 0);
   3686 		    OPEN_XFT(wnorm, "wide");
   3687 
   3688 		    if (XftFp(wnorm) != NULL) {
   3689 			wbold->pattern = XftPatternDuplicate(pat);
   3690 			XftPatternBuild(wbold->pattern,
   3691 					WideXftPattern,
   3692 					BoldXftPattern(wnorm),
   3693 					(void *) 0);
   3694 			OPEN_XFT(wbold, "wide-bold");
   3695 
   3696 #if HAVE_ITALICS
   3697 			if (FIND_ITALICS) {
   3698 			    wital->pattern = XftPatternDuplicate(pat);
   3699 			    XftPatternBuild(wital->pattern,
   3700 					    WideXftPattern,
   3701 					    ItalXftPattern(wnorm),
   3702 					    (void *) 0);
   3703 			    OPEN_XFT(wital, "wide-italic");
   3704 			}
   3705 			CACHE_XFT(wbtal);
   3706 			if (!XftFp(wbtal)) {
   3707 			    noUsableXft(xw, "wide bold");
   3708 			    XftPatternDestroy(wbtal->pattern);
   3709 			    wbtal->pattern = XftPatternDuplicate(pat);
   3710 			    XftPatternBuild(wbtal->pattern,
   3711 					    WideXftPattern,
   3712 					    (void *) 0);
   3713 			    OPEN_XFT(wbtal, "wide-bold-italics");
   3714 			    failed = 0;
   3715 			    CACHE_XFT(wbtal);
   3716 			}
   3717 #endif
   3718 		    }
   3719 
   3720 		    CACHE_XFT(wnorm);
   3721 
   3722 		    CACHE_XFT(wbold);
   3723 		    if (XftFp(wnorm) != NULL && !XftFp(wbold)) {
   3724 			noUsableXft(xw, "wide-bold");
   3725 			XftPatternDestroy(wbold->pattern);
   3726 			wbold->pattern = XftPatternDuplicate(pat);
   3727 			XftPatternBuild(bold->pattern,
   3728 					WideXftPattern,
   3729 					(void *) 0);
   3730 			OPEN_XFT(wbold, "wide-bold");
   3731 			failed = 0;
   3732 			CACHE_XFT(bold);
   3733 		    }
   3734 
   3735 		    CACHE_XFT(wital);
   3736 		    if (XftFp(wnorm) != NULL && !XftFp(wital)) {
   3737 			noUsableXft(xw, "wide-italic");
   3738 			XftPatternDestroy(wital->pattern);
   3739 			wital->pattern = XftPatternDuplicate(pat);
   3740 			XftPatternBuild(wital->pattern,
   3741 					WideXftPattern,
   3742 					(void *) 0);
   3743 			OPEN_XFT(wital, "wide-italic");
   3744 			failed = 0;
   3745 			CACHE_XFT(wital);
   3746 		    }
   3747 
   3748 		    XftPatternDestroy(pat);
   3749 		}
   3750 #undef OPEN_XFT
   3751 	    }
   3752 #endif /* OPT_RENDERWIDE */
   3753 	}
   3754 	if (XftFp(norm) == NULL) {
   3755 	    TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum));
   3756 	    xw->work.render_font = False;
   3757 	    update_font_renderfont();
   3758 	    /* now we will fall through into the bitmap fonts */
   3759 	} else {
   3760 	    setBrokenBoxChars(xw, False);
   3761 	    setRenderFontsize(xw, win, norm, NULL);
   3762 	    setRenderFontsize(xw, win, bold, "bold");
   3763 	    setRenderFontsize(xw, win, ital, "ital");
   3764 	    setRenderFontsize(xw, win, btal, "btal");
   3765 #if OPT_BOX_CHARS
   3766 	    setupPackedFonts(xw);
   3767 
   3768 	    if (screen->force_packed) {
   3769 		XTermXftFonts *use = &(screen->renderFontNorm[fontnum]);
   3770 		SetFontHeight(screen, win, XftFp(use)->ascent + XftFp(use)->descent);
   3771 		SetFontWidth(screen, win, use->font_info.min_width);
   3772 		TRACE(("...packed TrueType font %dx%d vs %d\n",
   3773 		       win->f_height,
   3774 		       win->f_width,
   3775 		       use->font_info.max_width));
   3776 	    }
   3777 #endif
   3778 	    DUMP_XFT(xw, &(screen->renderFontNorm[fontnum]));
   3779 	}
   3780     }
   3781     /*
   3782      * Are we handling a bitmap font?
   3783      */
   3784     else
   3785 #endif /* OPT_RENDERFONT */
   3786     {
   3787 	if (is_double_width_font(font) && !(screen->fnt_prop)) {
   3788 	    SetFontWidth(screen, win, font->min_bounds.width);
   3789 	} else {
   3790 	    SetFontWidth(screen, win, font->max_bounds.width);
   3791 	}
   3792 	SetFontHeight(screen, win, font->ascent + font->descent);
   3793 	win->f_ascent = font->ascent;
   3794 	win->f_descent = font->descent;
   3795     }
   3796     i = 2 * screen->border + sbwidth;
   3797     j = 2 * screen->border;
   3798     width = MaxCols(screen) * win->f_width + i;
   3799     height = MaxRows(screen) * win->f_height + j;
   3800     win->fullwidth = (Dimension) width;
   3801     win->fullheight = (Dimension) height;
   3802     win->width = width - i;
   3803     win->height = height - j;
   3804 
   3805     TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
   3806 	   win->height,
   3807 	   win->width,
   3808 	   win->fullheight,
   3809 	   win->fullwidth,
   3810 	   win->f_height,
   3811 	   win->f_width,
   3812 	   win->f_ascent,
   3813 	   win->f_descent));
   3814 
   3815     checkFontInfo(win->f_height, "height", failed);
   3816     checkFontInfo(win->f_width, "width", failed);
   3817 }
   3818 
   3819 /* save this information as a side-effect for double-sized characters */
   3820 static void
   3821 xtermSaveFontInfo(TScreen *screen, XFontStruct *font)
   3822 {
   3823     screen->fnt_wide = (Dimension) (font->max_bounds.width);
   3824     screen->fnt_high = (Dimension) (font->ascent + font->descent);
   3825     TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
   3826 }
   3827 
   3828 /*
   3829  * After loading a new font, update the structures that use its size.
   3830  */
   3831 void
   3832 xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
   3833 {
   3834     TScreen *screen = TScreenOf(xw);
   3835 
   3836     int scrollbar_width;
   3837     VTwin *win = &(screen->fullVwin);
   3838 
   3839 #if USE_DOUBLE_BUFFER
   3840     discardRenderDraw(TScreenOf(xw));
   3841 #endif /* USE_DOUBLE_BUFFER */
   3842 
   3843     scrollbar_width = (xw->misc.scrollbar
   3844 		       ? (screen->scrollWidget->core.width +
   3845 			  BorderWidth(screen->scrollWidget))
   3846 		       : 0);
   3847     xtermComputeFontInfo(xw, win, GetNormalFont(screen, fNorm)->fs, scrollbar_width);
   3848     xtermSaveFontInfo(screen, GetNormalFont(screen, fNorm)->fs);
   3849 
   3850     if (doresize) {
   3851 	if (VWindow(screen)) {
   3852 	    xtermClear(xw);
   3853 	}
   3854 	TRACE(("xtermUpdateFontInfo " TRACE_L "\n"));
   3855 	DoResizeScreen(xw);	/* set to the new natural size */
   3856 	ResizeScrollBar(xw);
   3857 	Redraw();
   3858 	TRACE((TRACE_R " xtermUpdateFontInfo\n"));
   3859 #ifdef SCROLLBAR_RIGHT
   3860 	updateRightScrollbar(xw);
   3861 #endif
   3862     }
   3863     xtermSetCursorBox(screen);
   3864 }
   3865 
   3866 #if OPT_BOX_CHARS || OPT_REPORT_FONTS
   3867 
   3868 /*
   3869  * Returns true if the given character is missing from the specified font.
   3870  */
   3871 Bool
   3872 xtermMissingChar(unsigned ch, XTermFonts * font)
   3873 {
   3874     Bool result = False;
   3875     XFontStruct *fs = font->fs;
   3876     XCharStruct *pc = NULL;
   3877 
   3878     if (fs == NULL) {
   3879 	result = True;
   3880     } else if (fs->max_byte1 == 0) {
   3881 #if OPT_WIDE_CHARS
   3882 	if (ch < 256)
   3883 #endif
   3884 	{
   3885 	    CI_GET_CHAR_INFO_1D(fs, ch, pc);
   3886 	}
   3887     }
   3888 #if OPT_WIDE_CHARS
   3889     else {
   3890 	unsigned row = (ch >> 8);
   3891 	unsigned col = (ch & 0xff);
   3892 	CI_GET_CHAR_INFO_2D(fs, row, col, pc);
   3893     }
   3894 #endif
   3895 
   3896     if (pc == NULL || CI_NONEXISTCHAR(pc)) {
   3897 	TRACE2(("xtermMissingChar %#04x (!exists)\n", ch));
   3898 	result = True;
   3899     }
   3900     if (ch < MaxUChar) {
   3901 	font->known_missing[ch] = (Char) (result ? 2 : 1);
   3902     }
   3903     return result;
   3904 }
   3905 #endif
   3906 
   3907 #if OPT_BOX_CHARS || OPT_WIDE_CHARS
   3908 /*
   3909  * The grid is arbitrary, enough resolution that nothing's lost in
   3910  * initialization.
   3911  */
   3912 #define BOX_HIGH 60
   3913 #define BOX_WIDE 60
   3914 
   3915 #define MID_HIGH (BOX_HIGH/2)
   3916 #define MID_WIDE (BOX_WIDE/2)
   3917 
   3918 #define CHR_WIDE ((9*BOX_WIDE)/10)
   3919 #define CHR_HIGH ((9*BOX_HIGH)/10)
   3920 
   3921 /*
   3922  * ...since we'll scale the values anyway.
   3923  */
   3924 #define Scale_XY(n,d,f) ((int)(n) * ((int)(f))) / (d)
   3925 #define SCALED_X(n) Scale_XY(n, BOX_WIDE, font_width)
   3926 #define SCALED_Y(n) Scale_XY(n, BOX_HIGH, font_height)
   3927 #define SCALE_X(n) n = SCALED_X(n)
   3928 #define SCALE_Y(n) n = SCALED_Y(n)
   3929 
   3930 #define SEG(x0,y0,x1,y1) x0,y0, x1,y1
   3931 
   3932 /*
   3933  * Draw the given graphic character, if it is simple enough (i.e., a
   3934  * line-drawing character).
   3935  */
   3936 void
   3937 xtermDrawBoxChar(XTermDraw * params,
   3938 		 unsigned ch,
   3939 		 GC gc,
   3940 		 int x,
   3941 		 int y,
   3942 		 int cells,
   3943 		 Bool xftords)
   3944 {
   3945     TScreen *screen = TScreenOf(params->xw);
   3946     /* *INDENT-OFF* */
   3947     static const short glyph_ht[] = {
   3948 	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,5*MID_HIGH/6),	/* H */
   3949 	SEG(6*BOX_WIDE/10,  0,		6*BOX_WIDE/10,5*MID_HIGH/6),
   3950 	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
   3951 	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
   3952 	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
   3953 	-1
   3954     }, glyph_ff[] = {
   3955 	SEG(1*BOX_WIDE/10,  0,		6*BOX_WIDE/10,	0),		/* F */
   3956 	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
   3957 	SEG(1*BOX_WIDE/10,  0,		0*BOX_WIDE/3, 5*MID_HIGH/6),
   3958 	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
   3959 	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
   3960 	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
   3961 	-1
   3962     }, glyph_lf[] = {
   3963 	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,9*MID_HIGH/12),	/* L */
   3964 	SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
   3965 	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
   3966 	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
   3967 	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
   3968 	-1
   3969     }, glyph_nl[] = {
   3970 	SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10,	0),		/* N */
   3971 	SEG(1*BOX_WIDE/10,  0,		5*BOX_WIDE/6, 5*MID_HIGH/6),
   3972 	SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
   3973 	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),	/* L */
   3974 	SEG(1*BOX_WIDE/3,   CHR_HIGH,	  CHR_WIDE,	CHR_HIGH),
   3975 	-1
   3976     }, glyph_vt[] = {
   3977 	SEG(1*BOX_WIDE/10,   0,		5*BOX_WIDE/12,5*MID_HIGH/6),	/* V */
   3978 	SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
   3979 	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
   3980 	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
   3981 	-1
   3982     }, plus_or_minus[] =
   3983     {
   3984 	SEG(  0,	  5*BOX_HIGH/6,	  CHR_WIDE,   5*BOX_HIGH/6),
   3985 	SEG(  MID_WIDE,	  2*BOX_HIGH/6,	  MID_WIDE,   4*BOX_HIGH/6),
   3986 	SEG(  0,	  3*BOX_HIGH/6,	  CHR_WIDE,   3*BOX_HIGH/6),
   3987 	-1
   3988     }, lower_right_corner[] =
   3989     {
   3990 	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
   3991 	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
   3992 	-1
   3993     }, upper_right_corner[] =
   3994     {
   3995 	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
   3996 	SEG( MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
   3997 	-1
   3998     }, upper_left_corner[] =
   3999     {
   4000 	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
   4001 	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
   4002 	-1
   4003     }, lower_left_corner[] =
   4004     {
   4005 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
   4006 	SEG(  MID_WIDE,	    MID_WIDE,	  BOX_WIDE,	MID_HIGH),
   4007 	-1
   4008     }, cross[] =
   4009     {
   4010 	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
   4011 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
   4012 	-1
   4013     }, scan_line_1[] =
   4014     {
   4015 	SEG(  0,	    0,		  BOX_WIDE,	0),
   4016 	-1
   4017     }, scan_line_3[] =
   4018     {
   4019 	SEG(  0,	    BOX_HIGH/4,	  BOX_WIDE,	BOX_HIGH/4),
   4020 	-1
   4021     }, scan_line_7[] =
   4022     {
   4023 	SEG( 0,		    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
   4024 	-1
   4025     }, scan_line_9[] =
   4026     {
   4027 	SEG(  0,	  3*BOX_HIGH/4,	  BOX_WIDE,   3*BOX_HIGH/4),
   4028 	-1
   4029     }, horizontal_line[] =
   4030     {
   4031 	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	BOX_HIGH),
   4032 	-1
   4033     }, left_tee[] =
   4034     {
   4035 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
   4036 	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
   4037 	-1
   4038     }, right_tee[] =
   4039     {
   4040 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
   4041 	SEG(  MID_WIDE,	    MID_HIGH,	  0,		MID_HIGH),
   4042 	-1
   4043     }, bottom_tee[] =
   4044     {
   4045 	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
   4046 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
   4047 	-1
   4048     }, top_tee[] =
   4049     {
   4050 	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
   4051 	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
   4052 	-1
   4053     }, vertical_line[] =
   4054     {
   4055 	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
   4056 	-1
   4057     }, less_than_or_equal[] =
   4058     {
   4059 	SEG(  CHR_WIDE,	    BOX_HIGH/3,	  0,		MID_HIGH),
   4060 	SEG(  CHR_WIDE,	  2*BOX_HIGH/3,	  0,		MID_HIGH),
   4061 	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
   4062 	-1
   4063     }, greater_than_or_equal[] =
   4064     {
   4065 	SEG(  0,	    BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
   4066 	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
   4067 	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
   4068 	-1
   4069     }, greek_pi[] =
   4070     {
   4071 	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
   4072 	SEG(5*CHR_WIDE/6,   MID_HIGH,	5*CHR_WIDE/6,	CHR_HIGH),
   4073 	SEG(2*CHR_WIDE/6,   MID_HIGH,	2*CHR_WIDE/6,	CHR_HIGH),
   4074 	-1
   4075     }, not_equal_to[] =
   4076     {
   4077 	SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3,	CHR_HIGH),
   4078 	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,   2*BOX_HIGH/3),
   4079 	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
   4080 	-1
   4081     }, sigma_1[] =
   4082     {
   4083 	SEG(BOX_WIDE,	    MID_HIGH,	  BOX_WIDE/2,	MID_HIGH),
   4084 	SEG(BOX_WIDE/2,	    MID_HIGH,	  BOX_WIDE,	BOX_HIGH),
   4085 	-1
   4086     }, sigma_2[] =
   4087     {
   4088 	SEG(BOX_WIDE,	    MID_HIGH,	  BOX_WIDE/2,	MID_HIGH),
   4089 	SEG(BOX_WIDE/2,	    MID_HIGH,	  BOX_WIDE,	0),
   4090 	-1
   4091     }, sigma_3[] =
   4092     {
   4093 	SEG(  0,	    0,	  	  BOX_WIDE,	BOX_HIGH),
   4094 	-1
   4095     }, sigma_4[] =
   4096     {
   4097 	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	0),
   4098 	-1
   4099     }, sigma_5[] =
   4100     {
   4101 	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
   4102 	SEG(MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
   4103 	-1
   4104     }, sigma_6[] =
   4105     {
   4106 	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
   4107 	SEG(MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
   4108 	-1
   4109     }, sigma_7[] =
   4110     {
   4111 	SEG(  0,	    0,		  MID_WIDE,	MID_HIGH),
   4112 	SEG(  0,	    BOX_HIGH,	  MID_WIDE,	MID_HIGH),
   4113 	-1
   4114     };
   4115 
   4116     static const struct {
   4117 	const int mode;			/* 1=y, 2=x, 3=both */
   4118 	const short *const data;
   4119     } lines[] =
   4120     {
   4121 	{ 0, NULL },			/* 00 (unused) */
   4122 	{ 0, NULL },			/* 01 diamond */
   4123 	{ 0, NULL },			/* 02 box */
   4124 	{ 0, glyph_ht },		/* 03 HT */
   4125 	{ 0, glyph_ff },		/* 04 FF */
   4126 	{ 0, NULL },			/* 05 CR */
   4127 	{ 0, glyph_lf },		/* 06 LF */
   4128 	{ 0, NULL },			/* 07 degrees (small circle) */
   4129 	{ 3, plus_or_minus },		/* 08 */
   4130 	{ 0, glyph_nl },		/* 09 */
   4131 	{ 0, glyph_vt },		/* 0A */
   4132 	{ 3, lower_right_corner },	/* 0B */
   4133 	{ 3, upper_right_corner },	/* 0C */
   4134 	{ 3, upper_left_corner },	/* 0D */
   4135 	{ 3, lower_left_corner },	/* 0E */
   4136 	{ 3, cross },			/* 0F */
   4137 	{ 2, scan_line_1 },		/* 10 */
   4138 	{ 2, scan_line_3 },		/* 11 */
   4139 	{ 2, scan_line_7 },		/* 12 */
   4140 	{ 2, scan_line_9 },		/* 13 */
   4141 	{ 2, horizontal_line },		/* 14 */
   4142 	{ 3, left_tee },		/* 15 */
   4143 	{ 3, right_tee },		/* 16 */
   4144 	{ 3, bottom_tee },		/* 17 */
   4145 	{ 3, top_tee },			/* 18 */
   4146 	{ 1, vertical_line },		/* 19 */
   4147 	{ 0, less_than_or_equal },	/* 1A */
   4148 	{ 0, greater_than_or_equal },	/* 1B */
   4149 	{ 0, greek_pi },		/* 1C */
   4150 	{ 0, not_equal_to },		/* 1D */
   4151 	{ 0, NULL },			/* 1E LB */
   4152 	{ 0, NULL },			/* 1F bullet */
   4153 	{ 0, NULL },			/* 20 space */
   4154 	{ 3, sigma_1 },			/* PUA(0) */
   4155 	{ 3, sigma_2 },			/* PUA(1) */
   4156 	{ 3, sigma_3 },			/* PUA(2) */
   4157 	{ 3, sigma_4 },			/* PUA(3) */
   4158 	{ 3, sigma_5 },			/* PUA(4) */
   4159 	{ 3, sigma_6 },			/* PUA(5) */
   4160 	{ 3, sigma_7 },			/* PUA(6) */
   4161     };
   4162     /* *INDENT-ON* */
   4163 
   4164     GC gc2;
   4165     CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
   4166     VTwin *cgsWin = WhichVWin(screen);
   4167     const short *p;
   4168     unsigned font_width = (((params->draw_flags & DOUBLEWFONT) ? 2U : 1U)
   4169 			   * screen->fnt_wide);
   4170     unsigned font_height = (((params->draw_flags & DOUBLEHFONT) ? 2U : 1U)
   4171 			    * screen->fnt_high);
   4172     unsigned thick;
   4173 
   4174     if (cells > 1)
   4175 	font_width *= (unsigned) cells;
   4176 
   4177 #if OPT_WIDE_CHARS
   4178     /*
   4179      * Try to show line-drawing characters if we happen to be in UTF-8
   4180      * mode, but have gotten an old-style font.
   4181      */
   4182     if (screen->utf8_mode
   4183 #if OPT_RENDERFONT
   4184 	&& !UsingRenderFont(params->xw)
   4185 #endif
   4186 	&& (ch > 127)
   4187 	&& !is_UCS_SPECIAL(ch)) {
   4188 	int which = (params->attr_flags & BOLD) ? fBold : fNorm;
   4189 	unsigned n;
   4190 	for (n = 1; n < 32; n++) {
   4191 	    if (xtermMissingChar(n, XTermFontsRef(screen->fnts, which)))
   4192 		continue;
   4193 	    if (dec2ucs(screen, n) != ch)
   4194 		continue;
   4195 	    TRACE(("...use xterm-style linedrawing U+%04X ->%d\n", ch, n));
   4196 	    ch = n;
   4197 	    break;
   4198 	}
   4199     }
   4200 #endif
   4201 
   4202 #if OPT_VT52_MODE
   4203     if (!(screen->vtXX_level)) {
   4204 	switch (ch) {
   4205 	case 6:
   4206 	    ch = 7;
   4207 	    break;
   4208 	default:
   4209 	    ch = 256;
   4210 	    break;
   4211 	}
   4212     }
   4213 #endif
   4214 
   4215     /*
   4216      * Line-drawing characters display using the full (scaled) cellsize, while
   4217      * other characters should be shifted to center them vertically.
   4218      */
   4219     if (!xftords) {
   4220 	if ((ch < XtNumber(lines)) && (lines[ch].mode & 3) != 0) {
   4221 	    font_height = (unsigned) ((float) font_height * screen->scale_height);
   4222 	} else {
   4223 	    y += ScaleShift(screen);
   4224 	}
   4225     }
   4226 
   4227     if (xtermIsDecTechnical(ch)) {
   4228 	ch -= XTERM_PUA;
   4229 	ch += 33;
   4230     }
   4231 
   4232     TRACE(("DRAW_BOX(%02X) cell %dx%d at %d,%d%s\n",
   4233 	   ch, font_height, font_width, y, x,
   4234 	   ((ch >= XtNumber(lines))
   4235 	    ? "-BAD"
   4236 	    : "")));
   4237 
   4238     if (cgsId == gcDots) {
   4239 	setCgsFont(params->xw, cgsWin, cgsId, getCgsFont(params->xw, cgsWin, gc));
   4240 	setCgsFore(params->xw, cgsWin, cgsId, getCgsFore(params->xw, cgsWin, gc));
   4241 	setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
   4242     } else {
   4243 	setCgsFont(params->xw, cgsWin, cgsId, getCgsFont(params->xw, cgsWin, gc));
   4244 	setCgsFore(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
   4245 	setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
   4246     }
   4247     gc2 = getCgsGC(params->xw, cgsWin, cgsId);
   4248 
   4249     if (!(params->draw_flags & NOBACKGROUND)) {
   4250 	XFillRectangle(screen->display, VDrawable(screen), gc2, x, y,
   4251 		       font_width,
   4252 		       font_height);
   4253     }
   4254 
   4255     setCgsFont(params->xw, cgsWin, cgsId, getCgsFont(params->xw, cgsWin, gc));
   4256     setCgsFore(params->xw, cgsWin, cgsId, getCgsFore(params->xw, cgsWin, gc));
   4257     setCgsBack(params->xw, cgsWin, cgsId, getCgsBack(params->xw, cgsWin, gc));
   4258     gc2 = getCgsGC(params->xw, cgsWin, cgsId);
   4259 
   4260     thick = ((params->attr_flags & BOLD)
   4261 	     ? (Max((unsigned) screen->fnt_high / 12, 1))
   4262 	     : (Max((unsigned) screen->fnt_high / 16, 1)));
   4263     setXtermLineAttributes(screen->display, gc2,
   4264 			   thick,
   4265 			   ((ch < XtNumber(lines))
   4266 			    ? LineSolid
   4267 			    : LineOnOffDash));
   4268 
   4269     if (ch == 32) {		/* space! */
   4270 	;			/* boxing a missing space is pointless */
   4271     } else if (ch == 1) {	/* diamond */
   4272 	XPoint points[5];
   4273 	int npoints = 5, n;
   4274 
   4275 	points[0].x = MID_WIDE;
   4276 	points[0].y = BOX_HIGH / 4;
   4277 
   4278 	points[1].x = 8 * BOX_WIDE / 8;
   4279 	points[1].y = MID_HIGH;
   4280 
   4281 	points[2].x = points[0].x;
   4282 	points[2].y = 3 * BOX_HIGH / 4;
   4283 
   4284 	points[3].x = 0 * BOX_WIDE / 8;
   4285 	points[3].y = points[1].y;
   4286 
   4287 	points[4].x = points[0].x;
   4288 	points[4].y = points[0].y;
   4289 
   4290 	for (n = 0; n < npoints; ++n) {
   4291 	    points[n].x = (short) (SCALED_X(points[n].x));
   4292 	    points[n].y = (short) (SCALED_Y(points[n].y));
   4293 	    points[n].x = (short) (points[n].x + x);
   4294 	    points[n].y = (short) (points[n].y + y);
   4295 	}
   4296 
   4297 	XFillPolygon(screen->display,
   4298 		     VDrawable(screen), gc2,
   4299 		     points, npoints,
   4300 		     Convex, CoordModeOrigin);
   4301     } else if (ch == 7) {	/* degrees */
   4302 	unsigned width = (BOX_WIDE / 3);
   4303 	int x_coord = MID_WIDE - (int) (width / 2);
   4304 	int y_coord = MID_HIGH - (int) width;
   4305 
   4306 	SCALE_X(x_coord);
   4307 	SCALE_Y(y_coord);
   4308 	width = (unsigned) SCALED_X(width);
   4309 
   4310 	XDrawArc(screen->display,
   4311 		 VDrawable(screen), gc2,
   4312 		 x + x_coord, y + y_coord, width, width,
   4313 		 0,
   4314 		 360 * 64);
   4315     } else if (ch == 0x1f) {	/* bullet */
   4316 	unsigned width = 7 * BOX_WIDE / 10;
   4317 	int x_coord = MID_WIDE - (int) (width / 3);
   4318 	int y_coord = MID_HIGH - (int) (width / 3);
   4319 
   4320 	SCALE_X(x_coord);
   4321 	SCALE_Y(y_coord);
   4322 	width = (unsigned) SCALED_X(width);
   4323 
   4324 	XDrawArc(screen->display,
   4325 		 VDrawable(screen), gc2,
   4326 		 x + x_coord, y + y_coord, width, width,
   4327 		 0,
   4328 		 360 * 64);
   4329     } else if (ch < XtNumber(lines)
   4330 	       && (p = lines[ch].data) != NULL) {
   4331 	int coord[4];
   4332 	int n = 0;
   4333 	while (*p >= 0) {
   4334 	    coord[n++] = *p++;
   4335 	    if (n == 4) {
   4336 		SCALE_X(coord[0]);
   4337 		SCALE_Y(coord[1]);
   4338 		SCALE_X(coord[2]);
   4339 		SCALE_Y(coord[3]);
   4340 		XDrawLine(screen->display,
   4341 			  VDrawable(screen), gc2,
   4342 			  x + coord[0], y + coord[1],
   4343 			  x + coord[2], y + coord[3]);
   4344 		n = 0;
   4345 	    }
   4346 	}
   4347     } else if (screen->force_all_chars) {
   4348 	/* bounding rectangle, for debugging */
   4349 	if ((params->draw_flags & DOUBLEHFONT)) {
   4350 	    XRectangle clip;
   4351 
   4352 	    clip.x = 0;
   4353 	    clip.y = 0;
   4354 	    clip.width = (unsigned short) ((font_width - 1) + (unsigned) thick);
   4355 	    clip.height = (unsigned short) ((unsigned) FontHeight(screen) + thick);
   4356 
   4357 	    if ((params->draw_flags & DOUBLEFIRST)) {
   4358 		y -= (2 * FontDescent(screen));
   4359 		clip.height =
   4360 		    (unsigned short) (clip.height
   4361 				      - ((unsigned short) FontDescent(screen)));
   4362 	    } else {
   4363 		y -= FontHeight(screen);
   4364 		y += FontDescent(screen);
   4365 		clip.y = (short) FontHeight(screen);
   4366 	    }
   4367 	    XSetClipRectangles(screen->display, gc2, x, y, &clip, 1, Unsorted);
   4368 	}
   4369 	XDrawRectangle(screen->display, VDrawable(screen), gc2,
   4370 		       x + (int) thick, y + (int) thick,
   4371 		       font_width - (2 * thick),
   4372 		       font_height - (2 * thick));
   4373 	if ((params->draw_flags & DOUBLEHFONT)) {
   4374 	    XSetClipMask(screen->display, gc2, None);
   4375 	}
   4376     }
   4377     resetXtermLineAttributes(screen->display, gc2);
   4378 }
   4379 #endif /* OPT_BOX_CHARS || OPT_WIDE_CHARS */
   4380 
   4381 #if OPT_RENDERFONT
   4382 static int
   4383 checkXftGlyph(XtermWidget xw, XftFont *font, unsigned wc)
   4384 {
   4385     TScreen *screen = TScreenOf(xw);
   4386     int result = 0;
   4387     int expect;
   4388 
   4389     if ((expect = CharWidth(screen, wc)) > 0) {
   4390 	XGlyphInfo gi;
   4391 	int actual;
   4392 	int limit = (100 + xw->misc.limit_fontwidth);
   4393 
   4394 	XftTextExtents32(screen->display, font, &wc, 1, &gi);
   4395 	/*
   4396 	 * Some (more than a few) fonts are sloppy; allow 10% outside
   4397 	 * the bounding box to accommodate them.
   4398 	 */
   4399 	actual = ((gi.xOff * 100) >= (limit * FontWidth(screen))) ? 2 : 1;
   4400 	if (actual <= expect) {
   4401 	    /* allow double-cell if wcwidth agrees */
   4402 	    result = 1;
   4403 	} else {
   4404 	    /*
   4405 	     * Do not use this font for this specific character, but
   4406 	     * possibly other characters can be used.
   4407 	     */
   4408 	    result = -1;
   4409 	    TRACE(("SKIP U+%04X %d vs %d (%d vs %d) %s\n",
   4410 		   wc, gi.xOff, FontWidth(screen), actual, expect,
   4411 		   nameOfXftFont(font)));
   4412 	}
   4413     } else {
   4414 	result = 1;
   4415     }
   4416     return result;
   4417 }
   4418 
   4419 /*
   4420  * Check if the glyph is defined in the given font, and (try to) filter out
   4421  * cases where double-width glyphs are stuffed into a single-width outline.
   4422  */
   4423 static int
   4424 foundXftGlyph(XtermWidget xw, XTermXftFonts *data, int fontNum, unsigned wc)
   4425 {
   4426     XftFont *font = XftFpN(data, fontNum);
   4427     int result = 0;
   4428 
   4429     if (font != NULL) {
   4430 	if (!xtermXftMissing(xw, data, fontNum, font, wc)) {
   4431 
   4432 	    if (XftIsN(data, fontNum) == xcBogus) {
   4433 		;
   4434 	    } else if (XftIsN(data, fontNum) == xcOpened) {
   4435 		result = 1;
   4436 	    } else {
   4437 		result = checkXftGlyph(xw, font, wc);
   4438 	    }
   4439 	}
   4440     }
   4441     return result;
   4442 }
   4443 
   4444 static void
   4445 markXftOpened(XtermWidget xw, XTermXftFonts *which, int n, unsigned wc)
   4446 {
   4447     if (XftIsN(which, n) != xcOpened) {
   4448 	which->opened++;
   4449 	XftIsN(which, n) = xcOpened;
   4450 	/* XFT_DEBUG=3 will show useful context for this */
   4451 	if (getenv("XFT_DEBUG") != NULL) {
   4452 	    printf("%s: matched U+%04X in fontset #%d [%u:%u]\n",
   4453 		   ProgramName,
   4454 		   wc, n + 1,
   4455 		   which->opened,
   4456 		   xw->work.max_fontsets);
   4457 	}
   4458     }
   4459 }
   4460 
   4461 static char **
   4462 xftData2List(XtermWidget xw, const XTermXftFonts *fontData)
   4463 {
   4464     TScreen *screen = TScreenOf(xw);
   4465     VTFontList *lists = &xw->work.fonts.xft;
   4466     char **result = NULL;
   4467     int n = screen->menu_font_number;
   4468 
   4469     if (fontData == &screen->renderFontNorm[n])
   4470 	result = lists->list_n;
   4471     else if (fontData == &screen->renderFontBold[n])
   4472 	result = lists->list_b;
   4473     else if (fontData == &screen->renderFontItal[n])
   4474 	result = lists->list_i;
   4475     else if (fontData == &screen->renderFontBtal[n])
   4476 	result = lists->list_bi;
   4477 #if OPT_RENDERWIDE
   4478     if (fontData == &screen->renderWideNorm[n])
   4479 	result = lists->list_w;
   4480     else if (fontData == &screen->renderWideBold[n])
   4481 	result = lists->list_wb;
   4482     else if (fontData == &screen->renderWideItal[n])
   4483 	result = lists->list_wi;
   4484     else if (fontData == &screen->renderWideBtal[n])
   4485 	result = lists->list_wbi;
   4486 #endif
   4487     return result;
   4488 }
   4489 
   4490 static FcPattern *
   4491 mergeXftStyle(XtermWidget xw, FcPattern * myPattern, XTermXftFonts *fontData)
   4492 {
   4493     TScreen *screen = TScreenOf(xw);
   4494     Display *dpy = screen->display;
   4495     XftFont *given = XftFp(fontData);
   4496     XftResult mStatus;
   4497     int iValue;
   4498     double dValue;
   4499 
   4500     if (FcOK(FcPatternGetInteger(fontData->pattern, XFT_WEIGHT, 0, &iValue))) {
   4501 	FcPatternAddInteger(myPattern, XFT_WEIGHT, iValue);
   4502     }
   4503     if (FcOK(FcPatternGetInteger(fontData->pattern, XFT_SLANT, 0, &iValue))) {
   4504 	FcPatternAddInteger(myPattern, XFT_SLANT, iValue);
   4505     }
   4506     if (FcOK(FcPatternGetDouble(fontData->pattern, FC_ASPECT, 0, &dValue))) {
   4507 	FcPatternAddDouble(myPattern, FC_ASPECT, dValue);
   4508     }
   4509     if (FcOK(FcPatternGetDouble(fontData->pattern, XFT_SIZE, 0, &dValue))) {
   4510 	FcPatternAddDouble(myPattern, XFT_SIZE, dValue);
   4511     }
   4512     FcPatternAddBool(myPattern, FC_SCALABLE, FcTrue);
   4513     FcPatternAddInteger(myPattern, XFT_SPACING, XFT_MONO);
   4514     FcPatternAddInteger(myPattern, FC_CHAR_WIDTH, given->max_advance_width);
   4515 #ifdef FC_COLOR
   4516 #if !USE_FC_COLOR
   4517     FcPatternAddBool(myPattern, FC_COLOR, FcFalse);
   4518 #endif
   4519     FcPatternAddBool(myPattern, FC_OUTLINE, FcTrue);
   4520 #endif
   4521 
   4522     FcConfigSubstitute(NULL, myPattern, FcMatchPattern);
   4523     XftDefaultSubstitute(dpy, DefaultScreen(dpy), myPattern);
   4524 
   4525     return FcFontMatch(NULL, myPattern, &mStatus);
   4526 }
   4527 
   4528 /*
   4529  * Check if the given character has a glyph known to Xft.  If it is missing,
   4530  * try first to replace the font with a fallback that provides the glyph.
   4531  *
   4532  * Return -1 if nothing is found.  Otherwise, return the index in the cache.
   4533  */
   4534 int
   4535 findXftGlyph(XtermWidget xw, XTermXftFonts *fontData, unsigned wc)
   4536 {
   4537     TScreen *screen = TScreenOf(xw);
   4538     XftFont *given;
   4539     XftFont *actual = NULL;
   4540     FcResult status;
   4541     int n;
   4542     int result = -1;
   4543 
   4544     /* sanity-check */
   4545     if (fontData == NULL)
   4546 	return result;
   4547     given = XftFp(fontData);
   4548 
   4549     /* if fontsets are not wanted, just leave */
   4550     if (xw->work.max_fontsets == 0) {
   4551 	return result;
   4552     }
   4553 
   4554     /* ignore codes in private use areas */
   4555     if ((wc >= 0xe000 && wc <= 0xf8ff)
   4556 	|| (wc >= 0xf0000 && wc <= 0xffffd)
   4557 	|| (wc >= 0x100000 && wc <= 0x10fffd)) {
   4558 	return result;
   4559     }
   4560     /* the end of the BMP is reserved for non-characters */
   4561     if (wc >= 0xfff0 && wc <= 0xffff) {
   4562 	return result;
   4563     }
   4564 
   4565     /* initialize on the first call */
   4566     if (fontData->fontset == NULL && fontData->pattern != NULL) {
   4567 	FcFontSet *sortedFonts;
   4568 	FcPattern *myPattern;
   4569 	int j;
   4570 	char **my_list;
   4571 
   4572 	myPattern = FcPatternDuplicate(fontData->pattern);
   4573 
   4574 	FcPatternAddBool(myPattern, FC_SCALABLE, FcTrue);
   4575 	FcPatternAddInteger(myPattern, FC_CHAR_WIDTH, given->max_advance_width);
   4576 
   4577 	FcConfigSubstitute(FcConfigGetCurrent(),
   4578 			   myPattern,
   4579 			   FcMatchPattern);
   4580 	FcDefaultSubstitute(myPattern);
   4581 
   4582 	sortedFonts = FcFontSort(NULL, myPattern, FcTrue, NULL, &status);
   4583 
   4584 	fontData->fontset = FcFontSetCreate();
   4585 
   4586 	if (fontData->fontset == NULL
   4587 	    || !sortedFonts
   4588 	    || sortedFonts->nfont <= 0) {
   4589 	    xtermWarning("did not find any usable TrueType font\n");
   4590 	    return 0;
   4591 	}
   4592 
   4593 	/*
   4594 	 * Check if there are additional fonts in the XtermFontNames.xft for
   4595 	 * this font-data.
   4596 	 */
   4597 	if ((my_list = xftData2List(xw, fontData)) != NULL
   4598 	    && *++my_list != NULL) {
   4599 	    for (j = 0; my_list[j] != NULL; ++j) {
   4600 		FcPattern *extraPattern;
   4601 		if ((extraPattern = XftNameParse(my_list[j])) != NULL) {
   4602 		    FcPattern *match;
   4603 
   4604 		    match = mergeXftStyle(xw, extraPattern, fontData);
   4605 
   4606 		    if (match != NULL) {
   4607 			FcFontSetAdd(fontData->fontset, match);
   4608 		    }
   4609 		    FcPatternDestroy(extraPattern);
   4610 		}
   4611 	    }
   4612 	}
   4613 
   4614 	for (j = 0; j < sortedFonts->nfont; j++) {
   4615 	    FcPattern *font_pattern;
   4616 
   4617 	    font_pattern = FcFontRenderPrepare(FcConfigGetCurrent(),
   4618 					       myPattern,
   4619 					       sortedFonts->fonts[j]);
   4620 	    if (font_pattern) {
   4621 		FcFontSetAdd(fontData->fontset, font_pattern);
   4622 	    }
   4623 	}
   4624 
   4625 	FcFontSetSortDestroy(sortedFonts);
   4626 	FcPatternDestroy(myPattern);
   4627 
   4628 	fontData->fs_size = Min(MaxXftCache, fontData->fontset->nfont);
   4629     }
   4630     if (fontData->fontset != NULL && fontData->fs_size > 0) {
   4631 	XftFont *check;
   4632 	int empty = fontData->fs_size;
   4633 
   4634 	for (n = 1; n <= fontData->fs_size; ++n) {
   4635 	    XTermXftState usage = XftIsN(fontData, n);
   4636 	    if (usage == xcEmpty) {
   4637 		if (empty > n)
   4638 		    empty = n;
   4639 	    } else if (usage == xcOpened
   4640 		       || (usage == xcUnused
   4641 			   && (fontData->opened < xw->work.max_fontsets))) {
   4642 		check = XftFpN(fontData, n);
   4643 		if (foundXftGlyph(xw, fontData, (int) n, wc)) {
   4644 		    markXftOpened(xw, fontData, n, wc);
   4645 		    actual = check;
   4646 		    result = (int) n;
   4647 		    TRACE_FALLBACK(xw, "old", wc, result, actual);
   4648 		    break;
   4649 		}
   4650 	    }
   4651 	}
   4652 
   4653 	if ((actual == NULL)
   4654 	    && (empty <= fontData->fs_size)
   4655 	    && (fontData->opened < xw->work.max_fontsets)) {
   4656 	    FcPattern *myPattern = NULL;
   4657 	    FcPattern *myReport = NULL;
   4658 	    int defer = -1;
   4659 
   4660 	    if (empty == 0)	/* should not happen */
   4661 		empty++;
   4662 
   4663 	    for (n = empty; n <= fontData->fs_size; ++n) {
   4664 		int found;
   4665 		int nn = n - 1;
   4666 
   4667 		if (XftIsN(fontData, n) != xcEmpty) {
   4668 		    continue;
   4669 		}
   4670 		if (resource.reportFonts) {
   4671 		    if (myReport != NULL)
   4672 			FcPatternDestroy(myReport);
   4673 		    myReport = FcPatternDuplicate(fontData->fontset->fonts[nn]);
   4674 		}
   4675 		myPattern = FcPatternDuplicate(fontData->fontset->fonts[nn]);
   4676 		check = XftFontOpenPattern(screen->display, myPattern);
   4677 		(void) maybeXftCache(xw, check);
   4678 		XftFpN(fontData, n) = check;
   4679 		if (check == NULL) {
   4680 		    ;		/* shouldn't happen... */
   4681 		} else
   4682 #ifdef FC_COLOR
   4683 		if (isBogusXft(check)) {
   4684 		    XftIsN(fontData, n) = xcBogus;
   4685 		} else
   4686 #endif
   4687 		    if ((found = foundXftGlyph(xw, fontData, (int) n, wc))
   4688 			!= 0) {
   4689 		    markXftOpened(xw, fontData, n, wc);
   4690 		    reportXftFallbackFont(xw, fontData, (int) n, check, myReport);
   4691 		    if (found < 0) {
   4692 			if (defer < 0) {
   4693 			    defer = (int) n;
   4694 			    TRACE(("Deferring font choice #%d\n", n + 1));
   4695 			    continue;
   4696 			} else if (slowXftMissing(xw, check, wc)) {
   4697 			    TRACE(("Deferred, continuing  #%d\n", n + 1));
   4698 			    continue;
   4699 			}
   4700 		    } else if (defer >= 0) {
   4701 			defer = -1;
   4702 			TRACE(("Deferred, replacing %d with %d\n",
   4703 			       defer + 1, n + 1));
   4704 		    }
   4705 		    actual = check;
   4706 		    result = (int) n;
   4707 		    TRACE_FALLBACK(xw, "new", wc, result, actual);
   4708 		    break;
   4709 		} else {
   4710 		    if (defer >= 0
   4711 			&& !slowXftMissing(xw, check, wc)
   4712 			&& checkXftGlyph(xw, check, wc)) {
   4713 			XTermFontMap *font_map = &(fontData->font_map);
   4714 			TRACE(("checkrecover2 %d\n", n));
   4715 			markXftOpened(xw, fontData, n, wc);
   4716 			reportXftFallbackFont(xw, fontData, (int) n, check, myReport);
   4717 			actual = check;
   4718 			result = (int) n;
   4719 			TRACE_FALLBACK(xw, "fix", wc, result, actual);
   4720 			font_map->per_font[wc] = (XTfontNum) (result + 1);
   4721 			break;
   4722 		    } else {
   4723 			/*
   4724 			 * The slot is opened, but we are not using it yet.
   4725 			 */
   4726 			XftIsN(fontData, n) = xcUnused;
   4727 		    }
   4728 		}
   4729 	    }
   4730 	    if (myReport != NULL)
   4731 		FcPatternDestroy(myReport);
   4732 	}
   4733     }
   4734     return result;
   4735 }
   4736 
   4737 /*
   4738  * Check if the given character has a glyph known to Xft.  If it is missing,
   4739  * return true.
   4740  *
   4741  * see xc/lib/Xft/xftglyphs.c
   4742  */
   4743 Bool
   4744 xtermXftMissing(XtermWidget xw,
   4745 		XTermXftFonts *data,
   4746 		int fontNum,	/* 0=primary, 1+ is fallback */
   4747 		XftFont *font,	/* actual font if no data */
   4748 		unsigned wc)
   4749 {
   4750     Bool result = True;
   4751 
   4752     (void) xw;
   4753     if (data != NULL && font != NULL) {
   4754 	XTermFontMap *font_map = &(data->font_map);
   4755 	/*
   4756 	 * Each fallback font has one chance to be scanned/cached.
   4757 	 * We record in per_font[] the index of the first font containing a
   4758 	 * given glyph.
   4759 	 */
   4760 	if (font_map->depth <= fontNum) {
   4761 	    FcChar32 last = (xtermXftLastChar(font) | 255) + 1;
   4762 	    FcChar32 base;
   4763 	    FcChar32 nextPage;
   4764 	    FcChar32 map[FC_CHARSET_MAP_SIZE];
   4765 	    unsigned added = 0;
   4766 	    unsigned actual = 0;
   4767 
   4768 	    font_map->depth = (fontNum + 1);
   4769 	    /* allocate space */
   4770 	    if (last > font_map->last_char) {
   4771 		size_t need = (last * sizeof(XTfontNum));
   4772 		size_t c1st = (font_map->last_char * sizeof(XTfontNum));
   4773 		font_map->per_font = realloc(font_map->per_font, need);
   4774 		memset(font_map->per_font + font_map->last_char, 0, (need - c1st));
   4775 		font_map->last_char = last;
   4776 	    }
   4777 
   4778 	    /* scan new font */
   4779 	    base = FcCharSetFirstPage(font->charset, map, &nextPage);
   4780 	    do {
   4781 		unsigned row;
   4782 		unsigned col;
   4783 		FcChar32 bits;
   4784 		for (row = 0; row < FC_CHARSET_MAP_SIZE; ++row) {
   4785 		    bits = map[row];
   4786 		    for (col = 0; col < 32; ++col) {
   4787 			if ((bits & 1) != 0) {
   4788 			    actual++;
   4789 			    if (!font_map->per_font[base]) {
   4790 				font_map->per_font[base] = (Char) font_map->depth;
   4791 				++added;
   4792 			    }
   4793 			}
   4794 			bits >>= 1;
   4795 			++base;
   4796 		    }
   4797 		}
   4798 	    } while ((base = FcCharSetNextPage(font->charset, map,
   4799 					       &nextPage)) != FC_CHARSET_DONE);
   4800 	    (void) added;
   4801 	    (void) actual;
   4802 	    TRACE(("xtermXftMissing U+%04X #%-3d %6u added vs %6u of %6ld %s: %s\n",
   4803 		   wc,
   4804 		   font_map->depth,
   4805 		   added, actual,
   4806 		   font_map->last_char + 1,
   4807 		   whichXftFonts(xw, data),
   4808 		   nameOfXftFont(font)));
   4809 	}
   4810 	if (wc < font_map->last_char) {
   4811 	    result = (font_map->per_font[wc] != (fontNum + 1));
   4812 	}
   4813     }
   4814     return result;
   4815 }
   4816 #endif /* OPT_RENDERFONT */
   4817 
   4818 #if OPT_WIDE_CHARS
   4819 #define MY_UCS(ucs,dec) case ucs: result = dec; break
   4820 unsigned
   4821 ucs2dec(TScreen *screen, unsigned ch)
   4822 {
   4823     unsigned result = ch;
   4824 
   4825     (void) screen;
   4826     if ((ch > 127)
   4827 	&& !is_UCS_SPECIAL(ch)) {
   4828 #if OPT_VT52_MODE
   4829 	if (screen != NULL && !(screen->vtXX_level)) {
   4830 	    /*
   4831 	     * Intentionally empty: it would be possible to use the built-in
   4832 	     * line-drawing fallback in xtermDrawBoxChar(), but for testing
   4833 	     * ncurses, this is good enough.
   4834 	     */
   4835 	    ;
   4836 	} else
   4837 #endif
   4838 	    switch (ch) {
   4839 		MY_UCS(0x25ae, 0);	/* black vertical rectangle           */
   4840 		MY_UCS(0x25c6, 1);	/* black diamond                      */
   4841 		MY_UCS(0x2592, 2);	/* medium shade                       */
   4842 		MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation   */
   4843 		MY_UCS(0x240c, 4);	/* symbol for form feed               */
   4844 		MY_UCS(0x240d, 5);	/* symbol for carriage return         */
   4845 		MY_UCS(0x240a, 6);	/* symbol for line feed               */
   4846 		MY_UCS(0x00b0, 7);	/* degree sign                        */
   4847 		MY_UCS(0x00b1, 8);	/* plus-minus sign                    */
   4848 		MY_UCS(0x2424, 9);	/* symbol for newline                 */
   4849 		MY_UCS(0x240b, 10);	/* symbol for vertical tabulation     */
   4850 		MY_UCS(0x2518, 11);	/* box drawings light up and left     */
   4851 		MY_UCS(0x2510, 12);	/* box drawings light down and left   */
   4852 		MY_UCS(0x250c, 13);	/* box drawings light down and right  */
   4853 		MY_UCS(0x2514, 14);	/* box drawings light up and right    */
   4854 		MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
   4855 		MY_UCS(0x23ba, 16);	/* box drawings scan 1                */
   4856 		MY_UCS(0x23bb, 17);	/* box drawings scan 3                */
   4857 		MY_UCS(0x2500, 18);	/* box drawings light horizontal      */
   4858 		MY_UCS(0x23bc, 19);	/* box drawings scan 7                */
   4859 		MY_UCS(0x23bd, 20);	/* box drawings scan 9                */
   4860 		MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
   4861 		MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
   4862 		MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
   4863 		MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
   4864 		MY_UCS(0x2502, 25);	/* box drawings light vertical        */
   4865 		MY_UCS(0x2264, 26);	/* less-than or equal to              */
   4866 		MY_UCS(0x2265, 27);	/* greater-than or equal to           */
   4867 		MY_UCS(0x03c0, 28);	/* greek small letter pi              */
   4868 		MY_UCS(0x2260, 29);	/* not equal to                       */
   4869 		MY_UCS(0x00a3, 30);	/* pound sign                         */
   4870 		MY_UCS(0x00b7, 31);	/* middle dot                         */
   4871 	    }
   4872     }
   4873     return result;
   4874 }
   4875 
   4876 #undef  MY_UCS
   4877 #define MY_UCS(ucs,dec) case dec: result = ucs; break
   4878 
   4879 unsigned
   4880 dec2ucs(TScreen *screen, unsigned ch)
   4881 {
   4882     unsigned result = ch;
   4883 
   4884     (void) screen;
   4885     if (xtermIsDecGraphic(ch)) {
   4886 #if OPT_VT52_MODE
   4887 	if (screen != NULL && !(screen->vtXX_level)) {
   4888 	    switch (ch) {
   4889 		MY_UCS(0x0020, 0);	/* nbsp, treat as blank           */
   4890 		MY_UCS(0x0020, 1);	/* reserved, treat as blank       */
   4891 		MY_UCS(0x25ae, 2);	/* black vertical rectangle       */
   4892 		MY_UCS(0x215f, 3);	/* "1/"                           */
   4893 		MY_UCS(0x0020, 4);	/* "3/", not in Unicode, ignore   */
   4894 		MY_UCS(0x0020, 5);	/* "5/", not in Unicode, ignore   */
   4895 		MY_UCS(0x0020, 6);	/* "7/", not in Unicode, ignore   */
   4896 		MY_UCS(0x00b0, 7);	/* degree sign                    */
   4897 		MY_UCS(0x00b1, 8);	/* plus-minus sign                */
   4898 		MY_UCS(0x2192, 9);	/* right-arrow                    */
   4899 		MY_UCS(0x2026, 10);	/* ellipsis                       */
   4900 		MY_UCS(0x00f7, 11);	/* divide by                      */
   4901 		MY_UCS(0x2193, 12);	/* down arrow                     */
   4902 		MY_UCS(0x23ba, 13);	/* bar at scan 0                  */
   4903 		MY_UCS(0x23ba, 14);	/* bar at scan 1                  */
   4904 		MY_UCS(0x23bb, 15);	/* bar at scan 2                  */
   4905 		MY_UCS(0x23bb, 16);	/* bar at scan 3                  */
   4906 		MY_UCS(0x23bc, 17);	/* bar at scan 4                  */
   4907 		MY_UCS(0x23bc, 18);	/* bar at scan 5                  */
   4908 		MY_UCS(0x23bd, 19);	/* bar at scan 6                  */
   4909 		MY_UCS(0x23bd, 20);	/* bar at scan 7                  */
   4910 		MY_UCS(0x2080, 21);	/* subscript 0                    */
   4911 		MY_UCS(0x2081, 22);	/* subscript 1                    */
   4912 		MY_UCS(0x2082, 23);	/* subscript 2                    */
   4913 		MY_UCS(0x2083, 24);	/* subscript 3                    */
   4914 		MY_UCS(0x2084, 25);	/* subscript 4                    */
   4915 		MY_UCS(0x2085, 26);	/* subscript 5                    */
   4916 		MY_UCS(0x2086, 27);	/* subscript 6                    */
   4917 		MY_UCS(0x2087, 28);	/* subscript 7                    */
   4918 		MY_UCS(0x2088, 29);	/* subscript 8                    */
   4919 		MY_UCS(0x2089, 30);	/* subscript 9                    */
   4920 		MY_UCS(0x00b6, 31);	/* paragraph                      */
   4921 	    }
   4922 	} else
   4923 #endif
   4924 	    switch (ch) {
   4925 		MY_UCS(0x25ae, 0);	/* black vertical rectangle           */
   4926 		MY_UCS(0x25c6, 1);	/* black diamond                      */
   4927 		MY_UCS(0x2592, 2);	/* medium shade                       */
   4928 		MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation   */
   4929 		MY_UCS(0x240c, 4);	/* symbol for form feed               */
   4930 		MY_UCS(0x240d, 5);	/* symbol for carriage return         */
   4931 		MY_UCS(0x240a, 6);	/* symbol for line feed               */
   4932 		MY_UCS(0x00b0, 7);	/* degree sign                        */
   4933 		MY_UCS(0x00b1, 8);	/* plus-minus sign                    */
   4934 		MY_UCS(0x2424, 9);	/* symbol for newline                 */
   4935 		MY_UCS(0x240b, 10);	/* symbol for vertical tabulation     */
   4936 		MY_UCS(0x2518, 11);	/* box drawings light up and left     */
   4937 		MY_UCS(0x2510, 12);	/* box drawings light down and left   */
   4938 		MY_UCS(0x250c, 13);	/* box drawings light down and right  */
   4939 		MY_UCS(0x2514, 14);	/* box drawings light up and right    */
   4940 		MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
   4941 		MY_UCS(0x23ba, 16);	/* box drawings scan 1                */
   4942 		MY_UCS(0x23bb, 17);	/* box drawings scan 3                */
   4943 		MY_UCS(0x2500, 18);	/* box drawings light horizontal      */
   4944 		MY_UCS(0x23bc, 19);	/* box drawings scan 7                */
   4945 		MY_UCS(0x23bd, 20);	/* box drawings scan 9                */
   4946 		MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
   4947 		MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
   4948 		MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
   4949 		MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
   4950 		MY_UCS(0x2502, 25);	/* box drawings light vertical        */
   4951 		MY_UCS(0x2264, 26);	/* less-than or equal to              */
   4952 		MY_UCS(0x2265, 27);	/* greater-than or equal to           */
   4953 		MY_UCS(0x03c0, 28);	/* greek small letter pi              */
   4954 		MY_UCS(0x2260, 29);	/* not equal to                       */
   4955 		MY_UCS(0x00a3, 30);	/* pound sign                         */
   4956 		MY_UCS(0x00b7, 31);	/* middle dot                         */
   4957 	    }
   4958     }
   4959     return result;
   4960 }
   4961 
   4962 #endif /* OPT_WIDE_CHARS */
   4963 
   4964 #if OPT_RENDERFONT || OPT_SHIFT_FONTS
   4965 static int
   4966 lookupOneFontSize(XtermWidget xw, int fontnum)
   4967 {
   4968     TScreen *screen = TScreenOf(xw);
   4969 
   4970     if (screen->menu_font_sizes[fontnum] == 0) {
   4971 	XTermFonts fnt;
   4972 
   4973 	memset(&fnt, 0, sizeof(fnt));
   4974 	screen->menu_font_sizes[fontnum] = -1;
   4975 	if (xtermOpenFont(xw, screen->MenuFontName(fontnum), &fnt, NULL, True)) {
   4976 	    if (fontnum <= fontMenu_lastBuiltin
   4977 		|| strcmp(fnt.fn, DEFFONT)) {
   4978 		screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
   4979 		if (screen->menu_font_sizes[fontnum] <= 0)
   4980 		    screen->menu_font_sizes[fontnum] = -1;
   4981 	    }
   4982 	    xtermCloseFont(xw, &fnt);
   4983 	}
   4984     }
   4985     return (screen->menu_font_sizes[fontnum] > 0);
   4986 }
   4987 
   4988 /*
   4989  * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
   4990  */
   4991 static void
   4992 lookupFontSizes(XtermWidget xw)
   4993 {
   4994     int n;
   4995 
   4996     for (n = 0; n < NMENUFONTS; n++) {
   4997 	(void) lookupOneFontSize(xw, n);
   4998     }
   4999 }
   5000 #endif /* OPT_RENDERFONT || OPT_SHIFT_FONTS */
   5001 
   5002 #if OPT_RENDERFONT
   5003 static double
   5004 defaultFaceSize(void)
   5005 {
   5006     double result;
   5007     float value;
   5008 
   5009     if (sscanf(DEFFACESIZE, "%f", &value) == 1)
   5010 	result = (double) value;
   5011     else
   5012 	result = 14.0;
   5013     return result;
   5014 }
   5015 
   5016 static void
   5017 fillInFaceSize(XtermWidget xw, int fontnum)
   5018 {
   5019     TScreen *screen = TScreenOf(xw);
   5020     double face_size = (double) xw->misc.face_size[fontnum];
   5021 
   5022     if (face_size <= 0.0) {
   5023 #if OPT_SHIFT_FONTS
   5024 	/*
   5025 	 * If the user is switching font-sizes, make it follow by
   5026 	 * default the same ratios to the default as the fixed fonts
   5027 	 * would, for easy comparison.  There will be some differences
   5028 	 * since the fixed fonts have a variety of height/width ratios,
   5029 	 * but this is simpler than adding another resource value - and
   5030 	 * as noted above, the data for the fixed fonts are available.
   5031 	 */
   5032 	(void) lookupOneFontSize(xw, 0);
   5033 	if (fontnum == fontMenu_default) {
   5034 	    face_size = defaultFaceSize();
   5035 	} else if (lookupOneFontSize(xw, fontnum)
   5036 		   && (screen->menu_font_sizes[0]
   5037 		       != screen->menu_font_sizes[fontnum])) {
   5038 	    double ratio;
   5039 	    long num = screen->menu_font_sizes[fontnum];
   5040 	    long den = screen->menu_font_sizes[0];
   5041 
   5042 	    if (den <= 0)
   5043 		den = 1;
   5044 	    ratio = dimSquareRoot((double) num / (double) den);
   5045 
   5046 	    face_size = (ratio * (double) xw->misc.face_size[0]);
   5047 	    TRACE(("scaled[%d] using %3ld/%ld = %.2f -> %f\n",
   5048 		   fontnum, num, den, ratio, face_size));
   5049 	} else
   5050 #endif
   5051 	{
   5052 #define LikeBitmap(s) (((s) / 78.0) * (double) xw->misc.face_size[fontMenu_default])
   5053 	    switch (fontnum) {
   5054 	    case fontMenu_font1:
   5055 		face_size = LikeBitmap(2.0);
   5056 		break;
   5057 	    case fontMenu_font2:
   5058 		face_size = LikeBitmap(35.0);
   5059 		break;
   5060 	    case fontMenu_font3:
   5061 		face_size = LikeBitmap(60.0);
   5062 		break;
   5063 	    default:
   5064 		face_size = defaultFaceSize();
   5065 		break;
   5066 	    case fontMenu_font4:
   5067 		face_size = LikeBitmap(90.0);
   5068 		break;
   5069 	    case fontMenu_font5:
   5070 		face_size = LikeBitmap(135.0);
   5071 		break;
   5072 	    case fontMenu_font6:
   5073 		face_size = LikeBitmap(200.0);
   5074 		break;
   5075 	    case fontMenu_font7:
   5076 		face_size = LikeBitmap(240.0);
   5077 		break;
   5078 	    }
   5079 	    TRACE(("builtin[%d] -> %f\n", fontnum, face_size));
   5080 	}
   5081 	xw->misc.face_size[fontnum] = (float) face_size;
   5082     }
   5083 }
   5084 
   5085 /* no selection or escape */
   5086 #define NMENU_RENDERFONTS (fontMenu_lastBuiltin + 1)
   5087 
   5088 /*
   5089  * Workaround for breakage in font-packages - check if all of the bitmap font
   5090  * sizes are the same, and if we're using TrueType fonts.
   5091  */
   5092 static Boolean
   5093 useFaceSizes(XtermWidget xw)
   5094 {
   5095     Boolean result = False;
   5096 
   5097     TRACE(("useFaceSizes " TRACE_L "\n"));
   5098     if (UsingRenderFont(xw)) {
   5099 	Boolean nonzero = True;
   5100 	int n;
   5101 
   5102 	for (n = 0; n < NMENU_RENDERFONTS; ++n) {
   5103 	    if (xw->misc.face_size[n] <= 0.0f) {
   5104 		nonzero = False;
   5105 		break;
   5106 	    }
   5107 	}
   5108 	if (!nonzero) {
   5109 	    Boolean broken_fonts = True;
   5110 	    TScreen *screen = TScreenOf(xw);
   5111 	    long first;
   5112 
   5113 	    lookupFontSizes(xw);
   5114 	    first = screen->menu_font_sizes[0];
   5115 	    for (n = 0; n < NMENUFONTS; n++) {
   5116 		if (screen->menu_font_sizes[n] > 0
   5117 		    && screen->menu_font_sizes[n] != first) {
   5118 		    broken_fonts = False;
   5119 		    break;
   5120 		}
   5121 	    }
   5122 
   5123 	    if (broken_fonts) {
   5124 
   5125 		TRACE(("bitmap fonts are broken - set faceSize resources\n"));
   5126 		for (n = 0; n < NMENUFONTS; n++) {
   5127 		    fillInFaceSize(xw, n);
   5128 		}
   5129 
   5130 	    }
   5131 	}
   5132 	result = True;
   5133     }
   5134     TRACE((TRACE_R " useFaceSizes %d\n", result));
   5135     return result;
   5136 }
   5137 #endif /* OPT_RENDERFONT */
   5138 
   5139 #if OPT_SHIFT_FONTS
   5140 /*
   5141  * Find the index of a larger/smaller font (according to the sign of 'relative'
   5142  * and its magnitude), starting from the 'old' index.
   5143  */
   5144 int
   5145 lookupRelativeFontSize(XtermWidget xw, int old, int relative)
   5146 {
   5147     TScreen *screen = TScreenOf(xw);
   5148     int m = -1;
   5149 
   5150     TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative));
   5151     if (!IsIcon(screen)) {
   5152 #if OPT_RENDERFONT
   5153 	if (useFaceSizes(xw)) {
   5154 	    TRACE(("...using FaceSize\n"));
   5155 	    if (relative != 0) {
   5156 		int n;
   5157 		for (n = 0; n < NMENU_RENDERFONTS; ++n) {
   5158 		    fillInFaceSize(xw, n);
   5159 		    if (xw->misc.face_size[n] > 0 &&
   5160 			xw->misc.face_size[n] != xw->misc.face_size[old]) {
   5161 			int cmp_0 = ((xw->misc.face_size[n] >
   5162 				      xw->misc.face_size[old])
   5163 				     ? relative
   5164 				     : -relative);
   5165 			int cmp_m = ((m < 0)
   5166 				     ? 1
   5167 				     : ((xw->misc.face_size[n] <
   5168 					 xw->misc.face_size[m])
   5169 					? relative
   5170 					: -relative));
   5171 			if (cmp_0 > 0 && cmp_m > 0) {
   5172 			    m = n;
   5173 			}
   5174 		    }
   5175 		}
   5176 	    }
   5177 	} else
   5178 #endif
   5179 	{
   5180 	    TRACE(("...using bitmap areas\n"));
   5181 	    lookupFontSizes(xw);
   5182 	    if (relative != 0) {
   5183 		int n;
   5184 		for (n = 0; n < NMENUFONTS; ++n) {
   5185 		    if (screen->menu_font_sizes[n] > 0 &&
   5186 			screen->menu_font_sizes[n] !=
   5187 			screen->menu_font_sizes[old]) {
   5188 			int cmp_0 = ((screen->menu_font_sizes[n] >
   5189 				      screen->menu_font_sizes[old])
   5190 				     ? relative
   5191 				     : -relative);
   5192 			int cmp_m = ((m < 0)
   5193 				     ? 1
   5194 				     : ((screen->menu_font_sizes[n] <
   5195 					 screen->menu_font_sizes[m])
   5196 					? relative
   5197 					: -relative));
   5198 			if (cmp_0 > 0 && cmp_m > 0) {
   5199 			    m = n;
   5200 			}
   5201 		    }
   5202 		}
   5203 	    }
   5204 	}
   5205 	TRACE(("...new index %d\n", m));
   5206 	if (m >= 0) {
   5207 	    if (relative > 1)
   5208 		m = lookupRelativeFontSize(xw, m, relative - 1);
   5209 	    else if (relative < -1)
   5210 		m = lookupRelativeFontSize(xw, m, relative + 1);
   5211 	}
   5212     }
   5213     return m;
   5214 }
   5215 
   5216 /* ARGSUSED */
   5217 void
   5218 HandleLargerFont(Widget w,
   5219 		 XEvent *event GCC_UNUSED,
   5220 		 String *params GCC_UNUSED,
   5221 		 Cardinal *param_count GCC_UNUSED)
   5222 {
   5223     XtermWidget xw;
   5224 
   5225     TRACE(("Handle larger-vt-font for %p\n", (void *) w));
   5226     if ((xw = getXtermWidget(w)) != NULL) {
   5227 	if (xw->misc.shift_fonts) {
   5228 	    TScreen *screen = TScreenOf(xw);
   5229 	    int m;
   5230 
   5231 	    m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
   5232 	    if (m >= 0) {
   5233 		SetVTFont(xw, m, True, NULL);
   5234 	    } else {
   5235 		Bell(xw, XkbBI_MinorError, 0);
   5236 	    }
   5237 	}
   5238     }
   5239 }
   5240 
   5241 /* ARGSUSED */
   5242 void
   5243 HandleSmallerFont(Widget w,
   5244 		  XEvent *event GCC_UNUSED,
   5245 		  String *params GCC_UNUSED,
   5246 		  Cardinal *param_count GCC_UNUSED)
   5247 {
   5248     XtermWidget xw;
   5249 
   5250     TRACE(("Handle smaller-vt-font for %p\n", (void *) w));
   5251     if ((xw = getXtermWidget(w)) != NULL) {
   5252 	if (xw->misc.shift_fonts) {
   5253 	    TScreen *screen = TScreenOf(xw);
   5254 	    int m;
   5255 
   5256 	    m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
   5257 	    if (m >= 0) {
   5258 		SetVTFont(xw, m, True, NULL);
   5259 	    } else {
   5260 		Bell(xw, XkbBI_MinorError, 0);
   5261 	    }
   5262 	}
   5263     }
   5264 }
   5265 #endif /* OPT_SHIFT_FONTS */
   5266 
   5267 int
   5268 xtermGetFont(const char *param)
   5269 {
   5270     int fontnum;
   5271 
   5272     if (param == NULL)
   5273 	param = "";
   5274 
   5275     switch (param[0]) {
   5276     case 'd':
   5277     case 'D':
   5278     case '0':
   5279 	fontnum = fontMenu_default;
   5280 	break;
   5281     case '1':
   5282 	fontnum = fontMenu_font1;
   5283 	break;
   5284     case '2':
   5285 	fontnum = fontMenu_font2;
   5286 	break;
   5287     case '3':
   5288 	fontnum = fontMenu_font3;
   5289 	break;
   5290     case '4':
   5291 	fontnum = fontMenu_font4;
   5292 	break;
   5293     case '5':
   5294 	fontnum = fontMenu_font5;
   5295 	break;
   5296     case '6':
   5297 	fontnum = fontMenu_font6;
   5298 	break;
   5299     case '7':
   5300 	fontnum = fontMenu_font7;
   5301 	break;
   5302     case 'e':
   5303     case 'E':
   5304 	fontnum = fontMenu_fontescape;
   5305 	break;
   5306     case 's':
   5307     case 'S':
   5308 	fontnum = fontMenu_fontsel;
   5309 	break;
   5310     default:
   5311 	fontnum = -1;
   5312 	break;
   5313     }
   5314     TRACE(("xtermGetFont(%s) ->%d\n", param, fontnum));
   5315     return fontnum;
   5316 }
   5317 
   5318 /* ARGSUSED */
   5319 void
   5320 HandleSetFont(Widget w,
   5321 	      XEvent *event GCC_UNUSED,
   5322 	      String *params,
   5323 	      Cardinal *param_count)
   5324 {
   5325     XtermWidget xw;
   5326 
   5327     if ((xw = getXtermWidget(w)) != NULL) {
   5328 	int fontnum;
   5329 	VTFontNames fonts;
   5330 
   5331 	memset(&fonts, 0, sizeof(fonts));
   5332 
   5333 	if (*param_count == 0) {
   5334 	    fontnum = fontMenu_default;
   5335 	} else {
   5336 	    Cardinal maxparams = 1;	/* total number of params allowed */
   5337 	    int result = xtermGetFont(params[0]);
   5338 
   5339 	    switch (result) {
   5340 	    case fontMenu_default:	/* FALLTHRU */
   5341 	    case fontMenu_font1:	/* FALLTHRU */
   5342 	    case fontMenu_font2:	/* FALLTHRU */
   5343 	    case fontMenu_font3:	/* FALLTHRU */
   5344 	    case fontMenu_font4:	/* FALLTHRU */
   5345 	    case fontMenu_font5:	/* FALLTHRU */
   5346 	    case fontMenu_font6:	/* FALLTHRU */
   5347 	    case fontMenu_font7:	/* FALLTHRU */
   5348 		break;
   5349 	    case fontMenu_fontescape:
   5350 #if OPT_WIDE_CHARS
   5351 		maxparams = 5;
   5352 #else
   5353 		maxparams = 3;
   5354 #endif
   5355 		break;
   5356 	    case fontMenu_fontsel:
   5357 		maxparams = 2;
   5358 		break;
   5359 	    default:
   5360 		Bell(xw, XkbBI_MinorError, 0);
   5361 		return;
   5362 	    }
   5363 	    fontnum = result;
   5364 
   5365 	    if (*param_count > maxparams) {	/* see if extra args given */
   5366 		Bell(xw, XkbBI_MinorError, 0);
   5367 		return;
   5368 	    }
   5369 	    switch (*param_count) {	/* assign 'em */
   5370 #if OPT_WIDE_CHARS
   5371 	    case 5:
   5372 		fonts.f_wb = x_strdup(params[4]);
   5373 		/* FALLTHRU */
   5374 	    case 4:
   5375 		fonts.f_w = x_strdup(params[3]);
   5376 #endif
   5377 		/* FALLTHRU */
   5378 	    case 3:
   5379 		fonts.f_b = x_strdup(params[2]);
   5380 		/* FALLTHRU */
   5381 	    case 2:
   5382 		fonts.f_n = x_strdup(params[1]);
   5383 		break;
   5384 	    }
   5385 	}
   5386 
   5387 	SetVTFont(xw, fontnum, True, &fonts);
   5388     }
   5389 }
   5390 
   5391 Bool
   5392 SetVTFont(XtermWidget xw,
   5393 	  int which,
   5394 	  Bool doresize,
   5395 	  const VTFontNames * fonts)
   5396 {
   5397     TScreen *screen = TScreenOf(xw);
   5398     Bool result = False;
   5399 
   5400     TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
   5401 	   (fonts && fonts->f_n) ? fonts->f_n : "<null>",
   5402 	   (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
   5403 
   5404     if (IsIcon(screen)) {
   5405 	Bell(xw, XkbBI_MinorError, 0);
   5406     } else if (which >= 0 && which < NMENUFONTS) {
   5407 	VTFontNames new_fnames;
   5408 
   5409 	memset(&new_fnames, 0, sizeof(new_fnames));
   5410 	if (fonts != NULL)
   5411 	    new_fnames = *fonts;
   5412 
   5413 	if (which == fontMenu_fontsel) {	/* go get the selection */
   5414 	    result = FindFontSelection(xw, new_fnames.f_n, False);
   5415 	} else {
   5416 #define USE_CACHED(field, name) \
   5417 	    if (new_fnames.field == NULL) { \
   5418 		new_fnames.field = x_strdup(screen->menu_font_names[which][name]); \
   5419 		TRACE(("set new_fnames." #field " from menu_font_names[%d][" #name "] %s\n", \
   5420 		       which, NonNull(new_fnames.field))); \
   5421 	    } else { \
   5422 		TRACE(("set new_fnames." #field " reused\n")); \
   5423 	    }
   5424 #define SAVE_FNAME(field, name) \
   5425 	    if (new_fnames.field != NULL \
   5426 		&& (screen->menu_font_names[which][name] == NULL \
   5427 		 || strcmp(screen->menu_font_names[which][name], new_fnames.field))) { \
   5428 		TRACE(("updating menu_font_names[%d][" #name "] to \"%s\"\n", \
   5429 		       which, new_fnames.field)); \
   5430 		FREE_STRING(screen->menu_font_names[which][name]); \
   5431 		screen->menu_font_names[which][name] = x_strdup(new_fnames.field); \
   5432 	    }
   5433 
   5434 	    USE_CACHED(f_n, fNorm);
   5435 	    USE_CACHED(f_b, fBold);
   5436 #if OPT_WIDE_CHARS
   5437 	    USE_CACHED(f_w, fWide);
   5438 	    USE_CACHED(f_wb, fWBold);
   5439 #endif
   5440 	    if (xtermLoadFont(xw,
   5441 			      &new_fnames,
   5442 			      doresize, which)) {
   5443 		/*
   5444 		 * If successful, save the data so that a subsequent query via
   5445 		 * OSC-50 will return the expected values.
   5446 		 */
   5447 		SAVE_FNAME(f_n, fNorm);
   5448 		SAVE_FNAME(f_b, fBold);
   5449 #if OPT_WIDE_CHARS
   5450 		SAVE_FNAME(f_w, fWide);
   5451 		SAVE_FNAME(f_wb, fWBold);
   5452 #endif
   5453 		result = True;
   5454 	    } else {
   5455 		Bell(xw, XkbBI_MinorError, 0);
   5456 	    }
   5457 	    FREE_FNAME(f_n);
   5458 	    FREE_FNAME(f_b);
   5459 #if OPT_WIDE_CHARS
   5460 	    FREE_FNAME(f_w);
   5461 	    FREE_FNAME(f_wb);
   5462 #endif
   5463 	}
   5464     } else {
   5465 	Bell(xw, XkbBI_MinorError, 0);
   5466     }
   5467     TRACE(("...SetVTFont: %d\n", result));
   5468     return result;
   5469 }
   5470 
   5471 #if OPT_RENDERFONT
   5472 static void
   5473 trimSizeFromFace(char *face_name, float *face_size)
   5474 {
   5475     char *first = strstr(face_name, ":size=");
   5476     if (first == NULL) {
   5477 	first = face_name;
   5478     } else {
   5479 	first++;
   5480     }
   5481     if (!strncmp(first, "size=", (size_t) 5)) {
   5482 	char *last = strchr(first, ':');
   5483 	char mark;
   5484 	float value;
   5485 	char extra;
   5486 	TRACE(("...before trimming, font = \"%s\"\n", face_name));
   5487 	if (last == NULL)
   5488 	    last = first + strlen(first);
   5489 	mark = *last;
   5490 	*last = '\0';
   5491 	if (sscanf(first, "size=%g%c", &value, &extra) == 1) {
   5492 	    TRACE(("...trimmed size from font: %g\n", value));
   5493 	    if (face_size != NULL)
   5494 		*face_size = value;
   5495 	}
   5496 	if (mark) {
   5497 	    while ((*first++ = *++last) != '\0') {
   5498 		;
   5499 	    }
   5500 	} else {
   5501 	    if (first != face_name)
   5502 		--first;
   5503 	    *first = '\0';
   5504 	}
   5505 	TRACE(("...after trimming, font = \"%s\"\n", face_name));
   5506     }
   5507 }
   5508 #endif
   5509 
   5510 /*
   5511  * Save a font specification to the proper list.
   5512  */
   5513 static void
   5514 save2FontList(XtermWidget xw,
   5515 	      const char *name,
   5516 	      XtermFontNames * fontnames,
   5517 	      VTFontEnum which,
   5518 	      const char *source,
   5519 	      Bool check,
   5520 	      Bool ttf)
   5521 {
   5522     char *value;
   5523     size_t plen;
   5524     Bool marked = False;
   5525     Bool use_ttf = ttf;
   5526 
   5527     (void) xw;
   5528 
   5529     if (source == NULL)
   5530 	source = "";
   5531     while (isspace(CharOf(*source)))
   5532 	++source;
   5533 
   5534     /* fontconfig patterns can contain ':' separators, but we'll treat
   5535      * a leading prefix specially to denote whether the pattern might be
   5536      * XLFD ("x" or "xlfd") versus Xft ("xft").
   5537      */
   5538     for (plen = 0; source[plen] != '\0'; ++plen) {
   5539 	if (source[plen] == ':') {
   5540 	    marked = True;
   5541 	    switch (plen) {
   5542 	    case 0:
   5543 		++plen;		/* trim leading ':' */
   5544 		break;
   5545 	    case 1:
   5546 		if (!strncmp(source, "x", plen)) {
   5547 		    ++plen;
   5548 		    use_ttf = False;
   5549 		} else {
   5550 		    marked = False;
   5551 		}
   5552 		break;
   5553 	    case 3:
   5554 		if (!strncmp(source, "xft", plen)) {
   5555 		    ++plen;
   5556 		    use_ttf = True;
   5557 		} else {
   5558 		    marked = False;
   5559 		}
   5560 		break;
   5561 	    case 4:
   5562 		if (!strncmp(source, "xlfd", plen)) {
   5563 		    ++plen;
   5564 		    use_ttf = False;
   5565 		} else {
   5566 		    marked = False;
   5567 		}
   5568 		break;
   5569 	    default:
   5570 		marked = False;
   5571 		plen = 0;
   5572 		break;
   5573 	    }
   5574 	    break;
   5575 	}
   5576     }
   5577     if (!marked)
   5578 	plen = 0;
   5579     value = x_strtrim(source + plen);
   5580     if (value != NULL) {
   5581 	Bool success = False;
   5582 #if OPT_RENDERFONT
   5583 	VTFontList *target = (use_ttf
   5584 			      ? &(fontnames->xft)
   5585 			      : &(fontnames->x11));
   5586 #else
   5587 	VTFontList *target = &(fontnames->x11);
   5588 #endif
   5589 	char ***list = NULL;
   5590 	char **next = NULL;
   5591 	size_t count = 0;
   5592 
   5593 	(void) use_ttf;
   5594 	switch (which) {
   5595 	case fNorm:
   5596 	    list = &(target->list_n);
   5597 	    break;
   5598 	case fBold:
   5599 	    list = &(target->list_b);
   5600 	    break;
   5601 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   5602 	case fItal:
   5603 	    list = &(target->list_i);
   5604 	    break;
   5605 	case fBtal:
   5606 	    list = &(target->list_bi);
   5607 	    break;
   5608 #endif
   5609 #if OPT_WIDE_CHARS
   5610 	case fWide:
   5611 	    list = &(target->list_w);
   5612 	    break;
   5613 	case fWBold:
   5614 	    list = &(target->list_wb);
   5615 	    break;
   5616 	case fWItal:
   5617 	    list = &(target->list_wi);
   5618 	    break;
   5619 	case fWBtal:
   5620 	    list = &(target->list_wbi);
   5621 	    break;
   5622 #endif
   5623 	case fMAX:
   5624 	    list = NULL;
   5625 	    break;
   5626 	}
   5627 
   5628 	if (list != NULL) {
   5629 	    success = True;
   5630 	    if (*list != NULL) {
   5631 		while ((*list)[count] != NULL) {
   5632 		    if (IsEmpty((*list)[count])) {
   5633 			TRACE(("... initial %s\n", value));
   5634 			free((*list)[count]);
   5635 			break;
   5636 		    } else if (!strcmp(value, (*list)[count])) {
   5637 			TRACE(("... duplicate %s\n", value));
   5638 			success = False;
   5639 			break;
   5640 		    }
   5641 		    ++count;
   5642 		}
   5643 	    }
   5644 	    if (success) {
   5645 		next = (char **) realloc(*list, sizeof(char *) * (count + 2));
   5646 		if (next != NULL) {
   5647 #if OPT_RENDERFONT
   5648 		    if (use_ttf) {
   5649 			trimSizeFromFace(value,
   5650 					 (count == 0 && which == fNorm)
   5651 					 ? &(xw->misc.face_size[0])
   5652 					 : (float *) 0);
   5653 		    }
   5654 #endif
   5655 		    next[count++] = value;
   5656 		    next[count] = NULL;
   5657 		    *list = next;
   5658 		    TRACE(("... saved \"%s\" \"%s\" %lu:\"%s\"\n",
   5659 			   whichFontList(xw, target),
   5660 			   whichFontList2(xw, *list),
   5661 			   (unsigned long) count,
   5662 			   value));
   5663 		} else {
   5664 		    xtermWarning("realloc failure in save2FontList(%s)\n", name);
   5665 		    freeFontList(list);
   5666 		    success = False;
   5667 		}
   5668 	    }
   5669 	}
   5670 	if (success) {
   5671 #if (MAX_XFT_FONTS == MAX_XLFD_FONTS)
   5672 	    size_t limit = MAX_XFT_FONTS;
   5673 #else
   5674 	    size_t limit = use_ttf ? MAX_XFT_FONTS : MAX_XLFD_FONTS;
   5675 #endif
   5676 	    if (count > limit && *x_skip_blanks(value)) {
   5677 		if (check) {
   5678 		    xtermWarning("too many fonts for %s, ignoring %s\n",
   5679 				 whichFontEnum(which),
   5680 				 value);
   5681 		}
   5682 		if (list && *list) {
   5683 		    free((*list)[limit]);
   5684 		    (*list)[limit] = NULL;
   5685 		}
   5686 	    }
   5687 	} else {
   5688 	    free(value);
   5689 	}
   5690     }
   5691 }
   5692 
   5693 /*
   5694  * In principle, any of the font-name resources could be extended to be a list
   5695  * of font-names.  That would be bad for performance, but as a basis for an
   5696  * extension, parse the font-name as a comma-separated list, creating/updating
   5697  * an array of font-names.
   5698  */
   5699 void
   5700 allocFontList(XtermWidget xw,
   5701 	      const char *name,
   5702 	      XtermFontNames * target,
   5703 	      VTFontEnum which,
   5704 	      const char *source,
   5705 	      Bool ttf)
   5706 {
   5707     char *blob;
   5708 
   5709     blob = x_strdup(source);
   5710     if (blob != NULL) {
   5711 	int n;
   5712 	int pass;
   5713 	char **list = NULL;
   5714 
   5715 	TRACE(("allocFontList %s name=\"%s\" source=\"%s\"\n",
   5716 	       whichFontEnum(which), name, blob));
   5717 
   5718 	for (pass = 0; pass < 2; ++pass) {
   5719 	    unsigned count = 0;
   5720 	    if (pass)
   5721 		list[0] = blob;
   5722 	    for (n = 0; blob[n] != '\0'; ++n) {
   5723 		if (blob[n] == ',') {
   5724 		    ++count;
   5725 		    if (pass != 0) {
   5726 			blob[n] = '\0';
   5727 			list[count] = blob + n + 1;
   5728 		    }
   5729 		}
   5730 	    }
   5731 	    if (!pass) {
   5732 		if (count == 0 && *blob == '\0')
   5733 		    break;
   5734 		list = TypeCallocN(char *, count + 2);
   5735 		if (list == NULL)
   5736 		    break;
   5737 	    }
   5738 	}
   5739 	if (list) {
   5740 	    for (n = 0; list[n] != NULL; ++n) {
   5741 		if (*list[n]) {
   5742 		    save2FontList(xw, name, target, which, list[n], True, ttf);
   5743 		}
   5744 	    }
   5745 	    free(list);
   5746 	}
   5747     }
   5748     free(blob);
   5749 }
   5750 
   5751 static void
   5752 initFontList(XtermWidget xw,
   5753 	     const char *name,
   5754 	     XtermFontNames * target,
   5755 	     Bool ttf)
   5756 {
   5757     int which;
   5758 
   5759     TRACE(("initFontList(%s)\n", name));
   5760     for (which = 0; which < fMAX; ++which) {
   5761 	save2FontList(xw, name, target, (VTFontEnum) which, "", False, ttf);
   5762     }
   5763 }
   5764 
   5765 void
   5766 initFontLists(XtermWidget xw)
   5767 {
   5768     TRACE(("initFontLists\n"));
   5769     initFontList(xw, "x11 font", &(xw->work.fonts), False);
   5770 #if OPT_RENDERFONT
   5771     initFontList(xw, "xft font", &(xw->work.fonts), True);
   5772 #endif
   5773 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
   5774     initFontList(xw, "cached font",
   5775 		 &(xw->screen.cacheVTFonts.fonts), False);
   5776 #endif
   5777 }
   5778 
   5779 void
   5780 copyFontList(char ***targetp, char **source)
   5781 {
   5782     freeFontList(targetp);
   5783 
   5784     if (source != NULL) {
   5785 	int pass;
   5786 	size_t count;
   5787 
   5788 	for (pass = 0; pass < 2; ++pass) {
   5789 	    for (count = 0; source[count] != NULL; ++count) {
   5790 		if (pass)
   5791 		    (*targetp)[count] = x_strdup(source[count]);
   5792 	    }
   5793 	    if (!pass) {
   5794 		++count;
   5795 		*targetp = TypeCallocN(char *, count);
   5796 	    }
   5797 	}
   5798     } else {
   5799 	*targetp = TypeCallocN(char *, 2);
   5800 	(*targetp)[0] = x_strdup("");
   5801     }
   5802 }
   5803 
   5804 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
   5805 static Boolean
   5806 merge_sublist(char ***targetp, char **source)
   5807 {
   5808     Boolean result = False;
   5809     if ((*targetp == NULL || IsEmpty(**targetp)) && !IsEmpty(*source)) {
   5810 	copyFontList(targetp, source);
   5811 	result = True;
   5812     }
   5813     return result;
   5814 }
   5815 #endif
   5816 
   5817 void
   5818 freeFontList(char ***targetp)
   5819 {
   5820     if (targetp != NULL) {
   5821 	char **target = *targetp;
   5822 	if (target != NULL) {
   5823 	    int n;
   5824 	    for (n = 0; target[n] != NULL; ++n) {
   5825 		free(target[n]);
   5826 	    }
   5827 	    free(target);
   5828 	    *targetp = NULL;
   5829 	}
   5830     }
   5831 }
   5832 
   5833 void
   5834 freeFontLists(VTFontList * lists)
   5835 {
   5836     int which;
   5837 
   5838     TRACE(("freeFontLists\n"));
   5839     for (which = 0; which < fMAX; ++which) {
   5840 	char ***target = NULL;
   5841 	switch (which) {
   5842 	case fNorm:
   5843 	    target = &(lists->list_n);
   5844 	    break;
   5845 	case fBold:
   5846 	    target = &(lists->list_b);
   5847 	    break;
   5848 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   5849 	case fItal:
   5850 	    target = &(lists->list_i);
   5851 	    break;
   5852 	case fBtal:
   5853 	    target = &(lists->list_bi);
   5854 	    break;
   5855 #endif
   5856 #if OPT_WIDE_CHARS
   5857 	case fWide:
   5858 	    target = &(lists->list_w);
   5859 	    break;
   5860 	case fWBold:
   5861 	    target = &(lists->list_wb);
   5862 	    break;
   5863 	case fWItal:
   5864 	    target = &(lists->list_wi);
   5865 	    break;
   5866 	case fWBtal:
   5867 	    target = &(lists->list_wbi);
   5868 	    break;
   5869 #endif
   5870 	default:
   5871 	    target = NULL;
   5872 	    break;
   5873 	}
   5874 	freeFontList(target);
   5875     }
   5876 }
   5877 
   5878 /*
   5879  * Return a pointer to the XLFD font information for a given font class.
   5880  * XXX make this allocate the font on demand.
   5881  */
   5882 XTermFonts *
   5883 getNormalFont(TScreen *screen, int which)
   5884 {
   5885     XTermFonts *result = NULL;
   5886     if (which >= 0 && which < fMAX)
   5887 	result = GetNormalFont(screen, which);
   5888     return result;
   5889 }
   5890 
   5891 #if OPT_DEC_CHRSET
   5892 XTermFonts *
   5893 getDoubleFont(TScreen *screen, int which)
   5894 {
   5895     XTermFonts *result = NULL;
   5896     if ((int) which >= 0 && which < NUM_CHRSET)
   5897 	result = GetDoubleFont(screen, which);
   5898     return result;
   5899 }
   5900 
   5901 #if OPT_RENDERFONT
   5902 void
   5903 getDoubleXftFont(XTermDraw * params, XTermXftFonts *data, unsigned chrset, unsigned attr_flags)
   5904 {
   5905     XtermWidget xw = params->xw;
   5906     TScreen *screen = TScreenOf(xw);
   5907     XftPattern *top_pattern;
   5908     int fontnum = screen->menu_font_number;
   5909     const char *face_name = getFaceName(xw, False);
   5910 
   5911     if (chrset != CSET_SWL
   5912 	&& (top_pattern = XftNameParse(face_name)) != NULL) {
   5913 	double face_size = (double) xw->misc.face_size[fontnum];
   5914 	XftPattern *sub_pattern = XftPatternDuplicate(top_pattern);
   5915 	const char *category = "doublesize";
   5916 
   5917 	switch (chrset) {
   5918 	case CSET_DHL_TOP:
   5919 	    category = "DHL_TOP";
   5920 	    goto double_high;
   5921 	case CSET_DHL_BOT:
   5922 	    category = "DHL_BOT";
   5923 	  double_high:
   5924 	    face_size *= 2;
   5925 	    XftPatternBuild(sub_pattern,
   5926 			    NormXftPattern,
   5927 			    (void *) 0);
   5928 	    break;
   5929 	case CSET_DWL:
   5930 	    category = "DWL";
   5931 	    XftPatternBuild(sub_pattern,
   5932 			    NormXftPattern,
   5933 			    FC_ASPECT, XftTypeDouble, 2.0,
   5934 			    (void *) 0);
   5935 	    break;
   5936 	}
   5937 	if (attr_flags & BOLD) {
   5938 	    XftPatternBuild(sub_pattern,
   5939 			    XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD,
   5940 			    (void *) 0);
   5941 	}
   5942 	xtermOpenXft(xw, data, 0, face_name, sub_pattern, category);
   5943 	data->pattern = sub_pattern;
   5944     }
   5945 }
   5946 #endif
   5947 #endif /* OPT_DEC_CHRSET */
   5948 
   5949 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   5950 XTermFonts *
   5951 getItalicFont(TScreen *screen, int which)
   5952 {
   5953     XTermFonts *result = NULL;
   5954 #if OPT_WIDE_ATTRS
   5955     if (which >= 0 && which < fMAX)
   5956 	result = GetItalicFont(screen, which);
   5957 #else
   5958     (void) screen;
   5959     (void) which;
   5960 #endif
   5961     return result;
   5962 }
   5963 #endif
   5964 
   5965 #if OPT_RENDERFONT
   5966 /*
   5967  * This returns a pointer to everything known about a given Xft font.
   5968  * XXX make this allocate the font on demand.
   5969  */
   5970 XTermXftFonts *
   5971 getMyXftFont(XtermWidget xw, int which, int fontnum)
   5972 {
   5973     TScreen *screen = TScreenOf(xw);
   5974     XTermXftFonts *result = NULL;
   5975     if (fontnum >= 0 && fontnum < NMENUFONTS) {
   5976 	switch ((VTFontEnum) which) {
   5977 	case fNorm:
   5978 	    result = &(screen->renderFontNorm[fontnum]);
   5979 	    break;
   5980 	case fBold:
   5981 	    result = &(screen->renderFontBold[fontnum]);
   5982 	    break;
   5983 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   5984 	case fItal:
   5985 	    result = &(screen->renderFontItal[fontnum]);
   5986 	    break;
   5987 	case fBtal:
   5988 	    result = &(screen->renderFontBtal[fontnum]);
   5989 	    break;
   5990 #endif
   5991 #if OPT_WIDE_CHARS
   5992 	case fWide:
   5993 	    result = &(screen->renderWideNorm[fontnum]);
   5994 	    break;
   5995 	case fWBold:
   5996 	    result = &(screen->renderWideBold[fontnum]);
   5997 	    break;
   5998 	case fWItal:
   5999 	    result = &(screen->renderWideItal[fontnum]);
   6000 	    break;
   6001 	case fWBtal:
   6002 	    result = &(screen->renderWideBtal[fontnum]);
   6003 	    break;
   6004 #endif
   6005 	case fMAX:
   6006 	    break;
   6007 	}
   6008     }
   6009     return result;
   6010 }
   6011 
   6012 const char *
   6013 whichXftFonts(XtermWidget xw, const XTermXftFonts *data)
   6014 {
   6015     TScreen *screen = TScreenOf(xw);
   6016     const char *result = "?";
   6017 #define CHECK(name) ((data >= &(screen->name[0])) && (data < &(screen->name[NMENUFONTS]))) result = #name
   6018     if CHECK
   6019 	(renderFontNorm);
   6020     else if CHECK
   6021 	(renderFontBold);
   6022 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   6023     else if CHECK
   6024 	(renderFontItal);
   6025     else if CHECK
   6026 	(renderFontBtal);
   6027 #endif
   6028 #if OPT_WIDE_CHARS
   6029     else if CHECK
   6030 	(renderWideNorm);
   6031     else if CHECK
   6032 	(renderWideBold);
   6033     else if CHECK
   6034 	(renderWideItal);
   6035     else if CHECK
   6036 	(renderWideBtal);
   6037 #endif
   6038 #if OPT_DEC_CHRSET
   6039 #if OPT_RENDERFONT
   6040     else {
   6041 	int n;
   6042 	for (n = 0; n < NUM_CHRSET; ++n) {
   6043 	    if (data == &screen->double_xft_fonts[n]) {
   6044 		result = "double_xft_fonts";
   6045 		break;
   6046 	    }
   6047 	}
   6048     }
   6049 #endif
   6050 #endif /* OPT_DEC_CHRSET */
   6051     return result;
   6052 }
   6053 
   6054 XftFont *
   6055 getXftFont(XtermWidget xw, VTFontEnum which, int fontnum)
   6056 {
   6057     XTermXftFonts *data = getMyXftFont(xw, (int) which, fontnum);
   6058     XftFont *result = NULL;
   6059     if (data != NULL)
   6060 	result = XftFp(data);
   6061     return result;
   6062 }
   6063 #endif
   6064 
   6065 const char *
   6066 whichFontEnum(VTFontEnum value)
   6067 {
   6068     const char *result = "?";
   6069 #define DATA(name) case name: result = #name; break
   6070     switch (value) {
   6071 	DATA(fNorm);
   6072 	DATA(fBold);
   6073 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   6074 	DATA(fItal);
   6075 	DATA(fBtal);
   6076 #endif
   6077 #if OPT_WIDE_CHARS
   6078 	DATA(fWide);
   6079 	DATA(fWBold);
   6080 	DATA(fWItal);
   6081 	DATA(fWBtal);
   6082 #endif
   6083 	DATA(fMAX);
   6084     }
   6085 #undef DATA
   6086     return result;
   6087 }
   6088 
   6089 const char *
   6090 whichFontList(XtermWidget xw, const VTFontList * value)
   6091 {
   6092     const char *result = "?";
   6093     if (value == &(xw->work.fonts.x11))
   6094 	result = "x11_fontnames";
   6095 #if OPT_RENDERFONT
   6096     else if (value == &(xw->work.fonts.xft))
   6097 	result = "xft_fontnames";
   6098 #endif
   6099 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
   6100     else if (value == &(xw->screen.cacheVTFonts.fonts.x11))
   6101 	result = "cached_fontnames";
   6102 #endif
   6103     return result;
   6104 }
   6105 
   6106 static const char *
   6107 whichFontList2s(VTFontList * list, char **value)
   6108 {
   6109     const char *result = NULL;
   6110 #define DATA(name) if (value == (list->name)) result = #name
   6111     DATA(list_n);
   6112     DATA(list_b);
   6113 #if OPT_WIDE_ATTRS || OPT_RENDERWIDE
   6114     DATA(list_i);
   6115     DATA(list_bi);
   6116 #endif
   6117 #if OPT_WIDE_CHARS
   6118     DATA(list_w);
   6119     DATA(list_wb);
   6120     DATA(list_wi);
   6121     DATA(list_wbi);
   6122 #endif
   6123 #undef DATA
   6124     return result;
   6125 }
   6126 
   6127 const char *
   6128 whichFontList2(XtermWidget xw, char **value)
   6129 {
   6130     const char *result = NULL;
   6131 #define DATA(name) (result = whichFontList2s(&(xw->name), value))
   6132     if (DATA(work.fonts.x11) == NULL) {
   6133 #if OPT_RENDERFONT
   6134 	if (DATA(work.fonts.xft) == NULL)
   6135 #endif
   6136 #if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
   6137 	    if (DATA(screen.cacheVTFonts.fonts.x11) == NULL)
   6138 #endif
   6139 		result = "?";
   6140     }
   6141 #undef DATA
   6142     return result;
   6143 }
   6144