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