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