"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/fontutils.c" (15 Feb 2023, 159159 Bytes) of package /linux/misc/xterm-379.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fontutils.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 377_vs_379.

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