"Fossies" - the Fresh Open Source Software Archive

Member "xterm-368/fontutils.c" (2 Jun 2021, 147691 Bytes) of package /linux/misc/xterm-368.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: 367_vs_368.

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