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