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