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