fontutils.c revision 0bd37d32
1/* $XTermId: fontutils.c,v 1.387 2013/05/15 00:31:56 tom Exp $ */
2
3/*
4 * Copyright 1998-2011,2012 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 SetFontWidth(screen,dst,src)  (dst)->f_width = (src)
55#define SetFontHeight(screen,dst,src) (dst)->f_height = dimRound((screen)->scale_height * (float) (src))
56
57/* from X11/Xlibint.h - not all vendors install this file */
58#define CI_NONEXISTCHAR(cs) (((cs)->width == 0) && \
59			     (((cs)->rbearing|(cs)->lbearing| \
60			       (cs)->ascent|(cs)->descent) == 0))
61
62#define CI_GET_CHAR_INFO_1D(fs,col,def,cs) \
63{ \
64    cs = def; \
65    if (col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
66	if (fs->per_char == NULL) { \
67	    cs = &fs->min_bounds; \
68	} else { \
69	    cs = &fs->per_char[(col - fs->min_char_or_byte2)]; \
70	    if (CI_NONEXISTCHAR(cs)) cs = def; \
71	} \
72    } \
73}
74
75#define CI_GET_CHAR_INFO_2D(fs,row,col,def,cs) \
76{ \
77    cs = def; \
78    if (row >= fs->min_byte1 && row <= fs->max_byte1 && \
79	col >= fs->min_char_or_byte2 && col <= fs->max_char_or_byte2) { \
80	if (fs->per_char == NULL) { \
81	    cs = &fs->min_bounds; \
82	} else { \
83	    cs = &fs->per_char[((row - fs->min_byte1) * \
84				(fs->max_char_or_byte2 - \
85				 fs->min_char_or_byte2 + 1)) + \
86			       (col - fs->min_char_or_byte2)]; \
87	    if (CI_NONEXISTCHAR(cs)) cs = def; \
88	} \
89    } \
90}
91
92/*
93 * A structure to hold the relevant properties from a font
94 * we need to make a well formed font name for it.
95 */
96typedef struct {
97    /* registry, foundry, family */
98    char *beginning;
99    /* weight */
100    char *weight;
101    /* slant */
102    char *slant;
103    /* wideness */
104    char *wideness;
105    /* add style */
106    char *add_style;
107    int pixel_size;
108    char *point_size;
109    int res_x;
110    int res_y;
111    char *spacing;
112    int average_width;
113    /* charset registry, charset encoding */
114    char *end;
115} FontNameProperties;
116
117#if OPT_RENDERFONT
118static void fillInFaceSize(XtermWidget, int);
119#endif
120
121#if OPT_SHIFT_FONTS
122static int lookupOneFontSize(XtermWidget, int);
123#endif
124
125#if OPT_WIDE_CHARS
126static unsigned
127countGlyphs(XFontStruct * fp)
128{
129    unsigned count = 0;
130
131    if (fp != 0) {
132	if (fp->min_byte1 == 0 && fp->max_byte1 == 0) {
133	    count = fp->max_char_or_byte2 - fp->min_char_or_byte2;
134	} else if (fp->min_char_or_byte2 < 256
135		   && fp->max_char_or_byte2 < 256) {
136	    unsigned first = (fp->min_byte1 << 8) + fp->min_char_or_byte2;
137	    unsigned last = (fp->max_byte1 << 8) + fp->max_char_or_byte2;
138	    count = last + 1 - first;
139	}
140    }
141    return count;
142}
143
144/*
145 * Verify that the wide-bold font is at least a bold font with roughly as many
146 * glyphs as the wide font.  The counts should be the same, but settle for
147 * filtering out the worst of the font mismatches.
148 */
149static Bool
150compatibleWideCounts(XFontStruct * wfs, XFontStruct * wbfs)
151{
152    unsigned count_w = countGlyphs(wfs);
153    unsigned count_wb = countGlyphs(wbfs);
154    if (count_w <= 256 ||
155	count_wb <= 256 ||
156	((count_w / 4) * 3) > count_wb) {
157	TRACE(("...font server lied (count wide %u vs wide-bold %u)\n",
158	       count_w, count_wb));
159	return False;
160    }
161    return True;
162}
163#endif /* OPT_WIDE_CHARS */
164
165#if OPT_BOX_CHARS
166static void
167setupPackedFonts(XtermWidget xw)
168{
169    TScreen *screen = TScreenOf(xw);
170    Bool value = False;
171
172#if OPT_RENDERFONT
173#define MIXED(name) screen->name[fontnum].map.mixed
174    if (xw->work.render_font == True) {
175	int fontnum = screen->menu_font_number;
176
177	screen->allow_packing = (Boolean) (MIXED(renderFontNorm)
178					   || MIXED(renderFontBold)
179					   || MIXED(renderFontItal)
180#if OPT_RENDERWIDE
181					   || MIXED(renderWideNorm)
182					   || MIXED(renderWideBold)
183					   || MIXED(renderWideItal)
184#endif
185	    );
186#undef MIXED
187    }
188#endif /* OPT_RENDERFONT */
189
190    value = screen->allow_packing;
191
192    SetItemSensitivity(fontMenuEntries[fontMenu_font_packedfont].widget, value);
193}
194#endif
195
196/*
197 * Returns the fields from start to stop in a dash- separated string.  This
198 * function will modify the source, putting '\0's in the appropiate place and
199 * moving the beginning forward to after the '\0'
200 *
201 * This will NOT work for the last field (but we won't need it).
202 */
203static char *
204n_fields(char **source, int start, int stop)
205{
206    int i;
207    char *str, *str1;
208
209    /*
210     * find the start-1th dash
211     */
212    for (i = start - 1, str = *source; i; i--, str++)
213	if ((str = strchr(str, '-')) == 0)
214	    return 0;
215
216    /*
217     * find the stopth dash
218     */
219    for (i = stop - start + 1, str1 = str; i; i--, str1++)
220	if ((str1 = strchr(str1, '-')) == 0)
221	    return 0;
222
223    /*
224     * put a \0 at the end of the fields
225     */
226    *(str1 - 1) = '\0';
227
228    /*
229     * move source forward
230     */
231    *source = str1;
232
233    return str;
234}
235
236static Boolean
237check_fontname(const char *name)
238{
239    Boolean result = True;
240
241    if (IsEmpty(name)) {
242	TRACE(("fontname missing\n"));
243	result = False;
244    }
245    return result;
246}
247
248/*
249 * Gets the font properties from a given font structure.  We use the FONT name
250 * to find them out, since that seems easier.
251 *
252 * Returns a pointer to a static FontNameProperties structure
253 * or NULL on error.
254 */
255static FontNameProperties *
256get_font_name_props(Display * dpy, XFontStruct * fs, char **result)
257{
258    static FontNameProperties props;
259    static char *last_name;
260
261    XFontProp *fp;
262    int i;
263    Atom fontatom = XInternAtom(dpy, "FONT", False);
264    char *name = 0;
265    char *str;
266
267    /*
268     * first get the full font name
269     */
270    if (fontatom != 0) {
271	for (i = 0, fp = fs->properties; i < fs->n_properties; i++, fp++) {
272	    if (fp->name == fontatom) {
273		name = XGetAtomName(dpy, fp->card32);
274		break;
275	    }
276	}
277    }
278
279    if (name == 0)
280	return 0;
281
282    /*
283     * XGetAtomName allocates memory - don't leak
284     */
285    if (last_name != 0)
286	XFree(last_name);
287    last_name = name;
288
289    if (result != 0) {
290	if (!check_fontname(name))
291	    return 0;
292	if (*result != 0)
293	    free(*result);
294	*result = x_strdup(name);
295    }
296
297    /*
298     * Now split it up into parts and put them in
299     * their places. Since we are using parts of
300     * the original string, we must not free the Atom Name
301     */
302
303    /* registry, foundry, family */
304    if ((props.beginning = n_fields(&name, 1, 3)) == 0)
305	return 0;
306
307    /* weight is the next */
308    if ((props.weight = n_fields(&name, 1, 1)) == 0)
309	return 0;
310
311    /* slant */
312    if ((props.slant = n_fields(&name, 1, 1)) == 0)
313	return 0;
314
315    /* width */
316    if ((props.wideness = n_fields(&name, 1, 1)) == 0)
317	return 0;
318
319    /* add style */
320    if ((props.add_style = n_fields(&name, 1, 1)) == 0)
321	return 0;
322
323    /* pixel size */
324    if ((str = n_fields(&name, 1, 1)) == 0)
325	return 0;
326    if ((props.pixel_size = atoi(str)) == 0)
327	return 0;
328
329    /* point size */
330    if ((props.point_size = n_fields(&name, 1, 1)) == 0)
331	return 0;
332
333    /* res_x */
334    if ((str = n_fields(&name, 1, 1)) == 0)
335	return 0;
336    if ((props.res_x = atoi(str)) == 0)
337	return 0;
338
339    /* res_y */
340    if ((str = n_fields(&name, 1, 1)) == 0)
341	return 0;
342    if ((props.res_y = atoi(str)) == 0)
343	return 0;
344
345    /* spacing */
346    if ((props.spacing = n_fields(&name, 1, 1)) == 0)
347	return 0;
348
349    /* average width */
350    if ((str = n_fields(&name, 1, 1)) == 0)
351	return 0;
352    if ((props.average_width = atoi(str)) == 0)
353	return 0;
354
355    /* the rest: charset registry and charset encoding */
356    props.end = name;
357
358    return &props;
359}
360
361#define ALLOCHUNK(n) ((n | 127) + 1)
362
363static void
364alloca_fontname(char **result, size_t next)
365{
366    size_t last = (*result != 0) ? strlen(*result) : 0;
367    size_t have = (*result != 0) ? ALLOCHUNK(last) : 0;
368    size_t want = last + next + 2;
369
370    if (want >= have) {
371	want = ALLOCHUNK(want);
372	if (last != 0) {
373	    *result = TypeRealloc(char, want, *result);
374	} else {
375	    if ((*result = TypeMallocN(char, want)) != 0)
376		**result = '\0';
377	}
378    }
379}
380
381static void
382append_fontname_str(char **result, const char *value)
383{
384    if (value == 0)
385	value = "*";
386    alloca_fontname(result, strlen(value));
387    if (*result != 0) {
388	if (**result != '\0')
389	    strcat(*result, "-");
390	strcat(*result, value);
391    }
392}
393
394static void
395append_fontname_num(char **result, int value)
396{
397    if (value < 0) {
398	append_fontname_str(result, "*");
399    } else {
400	char temp[100];
401	sprintf(temp, "%d", value);
402	append_fontname_str(result, temp);
403    }
404}
405
406/*
407 * Take the given font props and try to make a well formed font name specifying
408 * the same base font and size and everything, but with different weight/width
409 * according to the parameters.  The return value is allocated, should be freed
410 * by the caller.
411 */
412static char *
413derive_font_name(FontNameProperties * props,
414		 const char *use_weight,
415		 int use_average_width,
416		 const char *use_encoding)
417{
418    char *result = 0;
419
420    append_fontname_str(&result, props->beginning);
421    append_fontname_str(&result, use_weight);
422    append_fontname_str(&result, props->slant);
423    append_fontname_str(&result, 0);
424    append_fontname_str(&result, 0);
425    append_fontname_num(&result, props->pixel_size);
426    append_fontname_str(&result, props->point_size);
427    append_fontname_num(&result, props->res_x);
428    append_fontname_num(&result, props->res_y);
429    append_fontname_str(&result, props->spacing);
430    append_fontname_num(&result, use_average_width);
431    append_fontname_str(&result, use_encoding);
432
433    return result;
434}
435
436static char *
437bold_font_name(FontNameProperties * props, int use_average_width)
438{
439    return derive_font_name(props, "bold", use_average_width, props->end);
440}
441
442#if OPT_WIDE_CHARS
443#define derive_wide_font(props, weight) \
444	derive_font_name(props, weight, props->average_width * 2, "ISO10646-1")
445
446static char *
447wide_font_name(FontNameProperties * props)
448{
449    return derive_wide_font(props, "medium");
450}
451
452static char *
453widebold_font_name(FontNameProperties * props)
454{
455    return derive_wide_font(props, "bold");
456}
457#endif /* OPT_WIDE_CHARS */
458
459#if OPT_DEC_CHRSET
460/*
461 * Take the given font props and try to make a well formed font name specifying
462 * the same base font but changed depending on the given attributes and chrset.
463 *
464 * For double width fonts, we just double the X-resolution, for double height
465 * fonts we double the pixel-size and Y-resolution
466 */
467char *
468xtermSpecialFont(TScreen * screen, unsigned atts, unsigned chrset)
469{
470#if OPT_TRACE
471    static char old_spacing[80];
472    static FontNameProperties old_props;
473#endif
474    FontNameProperties *props;
475    char *result = 0;
476    const char *weight;
477    int pixel_size;
478    int res_x;
479    int res_y;
480
481    props = get_font_name_props(screen->display, screen->fnts[fNorm].fs, 0);
482    if (props == 0)
483	return result;
484
485    pixel_size = props->pixel_size;
486    res_x = props->res_x;
487    res_y = props->res_y;
488    if (atts & BOLD)
489	weight = "bold";
490    else
491	weight = props->weight;
492
493    if (CSET_DOUBLE(chrset))
494	res_x *= 2;
495
496    if (chrset == CSET_DHL_TOP
497	|| chrset == CSET_DHL_BOT) {
498	res_y *= 2;
499	pixel_size *= 2;
500    }
501#if OPT_TRACE
502    if (old_props.res_x != res_x
503	|| old_props.res_x != res_y
504	|| old_props.pixel_size != pixel_size
505	|| strcmp(old_props.spacing, props->spacing)) {
506	TRACE(("xtermSpecialFont(atts = %#x, chrset = %#x)\n", atts, chrset));
507	TRACE(("res_x      = %d\n", res_x));
508	TRACE(("res_y      = %d\n", res_y));
509	TRACE(("point_size = %s\n", props->point_size));
510	TRACE(("pixel_size = %d\n", pixel_size));
511	TRACE(("spacing    = %s\n", props->spacing));
512	old_props.res_x = res_x;
513	old_props.res_x = res_y;
514	old_props.pixel_size = pixel_size;
515	old_props.spacing = old_spacing;
516	sprintf(old_spacing, "%.*s", (int) sizeof(old_spacing) - 2, props->spacing);
517    }
518#endif
519
520    append_fontname_str(&result, props->beginning);
521    append_fontname_str(&result, weight);
522    append_fontname_str(&result, props->slant);
523    append_fontname_str(&result, props->wideness);
524    append_fontname_str(&result, props->add_style);
525    append_fontname_num(&result, pixel_size);
526    append_fontname_str(&result, props->point_size);
527    append_fontname_num(&result, (atts & NORESOLUTION) ? -1 : res_x);
528    append_fontname_num(&result, (atts & NORESOLUTION) ? -1 : res_y);
529    append_fontname_str(&result, props->spacing);
530    append_fontname_str(&result, 0);
531    append_fontname_str(&result, props->end);
532
533    return result;
534}
535#endif /* OPT_DEC_CHRSET */
536
537/*
538 * Case-independent comparison for font-names, including wildcards.
539 * XLFD allows '?' as a wildcard, but we do not handle that (no one seems
540 * to use it).
541 */
542static Bool
543same_font_name(const char *pattern, const char *match)
544{
545    Bool result = False;
546
547    if (pattern && match) {
548	while (*pattern && *match) {
549	    if (*pattern == *match) {
550		pattern++;
551		match++;
552	    } else if (*pattern == '*' || *match == '*') {
553		if (same_font_name(pattern + 1, match)) {
554		    return True;
555		} else if (same_font_name(pattern, match + 1)) {
556		    return True;
557		} else {
558		    return False;
559		}
560	    } else {
561		int p = x_toupper(*pattern++);
562		int m = x_toupper(*match++);
563		if (p != m)
564		    return False;
565	    }
566	}
567	result = (*pattern == *match);	/* both should be NUL */
568    }
569    return result;
570}
571
572/*
573 * Double-check the fontname that we asked for versus what the font server
574 * actually gave us.  The larger fixed fonts do not always have a matching bold
575 * font, and the font server may try to scale another font or otherwise
576 * substitute a mismatched font.
577 *
578 * If we cannot get what we requested, we will fallback to the original
579 * behavior, which simulates bold by overstriking each character at one pixel
580 * offset.
581 */
582static int
583got_bold_font(Display * dpy, XFontStruct * fs, String requested)
584{
585    char *actual = 0;
586    int got;
587
588    if (get_font_name_props(dpy, fs, &actual) == 0)
589	got = 0;
590    else
591	got = same_font_name(requested, actual);
592    free(actual);
593    return got;
594}
595
596/*
597 * If the font server tries to adjust another font, it may not adjust it
598 * properly.  Check that the bounding boxes are compatible.  Otherwise we'll
599 * leave trash on the display when we mix normal and bold fonts.
600 */
601static int
602same_font_size(XtermWidget xw, XFontStruct * nfs, XFontStruct * bfs)
603{
604    TScreen *screen = TScreenOf(xw);
605    TRACE(("same_font_size height %d/%d, min %d/%d max %d/%d\n",
606	   nfs->ascent + nfs->descent,
607	   bfs->ascent + bfs->descent,
608	   nfs->min_bounds.width, bfs->min_bounds.width,
609	   nfs->max_bounds.width, bfs->max_bounds.width));
610    return screen->free_bold_box
611	|| ((nfs->ascent + nfs->descent) == (bfs->ascent + bfs->descent)
612	    && (nfs->min_bounds.width == bfs->min_bounds.width
613		|| nfs->min_bounds.width == bfs->min_bounds.width + 1)
614	    && (nfs->max_bounds.width == bfs->max_bounds.width
615		|| nfs->max_bounds.width == bfs->max_bounds.width + 1));
616}
617
618/*
619 * Check if the font looks like it has fixed width
620 */
621static int
622is_fixed_font(XFontStruct * fs)
623{
624    if (fs)
625	return (fs->min_bounds.width == fs->max_bounds.width);
626    return 1;
627}
628
629/*
630 * Check if the font looks like a double width font (i.e. contains
631 * characters of width X and 2X
632 */
633#if OPT_WIDE_CHARS
634static int
635is_double_width_font(XFontStruct * fs)
636{
637    return ((2 * fs->min_bounds.width) == fs->max_bounds.width);
638}
639#else
640#define is_double_width_font(fs) 0
641#endif
642
643#if OPT_WIDE_CHARS && OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
644#define HALF_WIDTH_TEST_STRING "1234567890"
645
646/* '1234567890' in Chinese characters in UTF-8 */
647#define FULL_WIDTH_TEST_STRING "\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89" \
648                               "\xe5\x9b\x9b\xe4\xba\x94" \
649			       "\xef\xa7\x91\xe4\xb8\x83\xe5\x85\xab" \
650			       "\xe4\xb9\x9d\xef\xa6\xb2"
651
652/* '1234567890' in Korean script in UTF-8 */
653#define FULL_WIDTH_TEST_STRING2 "\xec\x9d\xbc\xec\x9d\xb4\xec\x82\xbc" \
654                                "\xec\x82\xac\xec\x98\xa4" \
655			        "\xec\x9c\xa1\xec\xb9\xa0\xed\x8c\x94" \
656			        "\xea\xb5\xac\xec\x98\x81"
657
658#define HALF_WIDTH_CHAR1  0x0031	/* '1' */
659#define HALF_WIDTH_CHAR2  0x0057	/* 'W' */
660#define FULL_WIDTH_CHAR1  0x4E00	/* CJK Ideograph 'number one' */
661#define FULL_WIDTH_CHAR2  0xAC00	/* Korean script syllable 'Ka' */
662
663static Bool
664is_double_width_font_xft(Display * dpy, XftFont * font)
665{
666    XGlyphInfo gi1, gi2;
667    FcChar32 c1 = HALF_WIDTH_CHAR1, c2 = HALF_WIDTH_CHAR2;
668    String fwstr = FULL_WIDTH_TEST_STRING;
669    String hwstr = HALF_WIDTH_TEST_STRING;
670
671    /* Some Korean fonts don't have Chinese characters at all. */
672    if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR1)) {
673	if (!XftCharExists(dpy, font, FULL_WIDTH_CHAR2))
674	    return False;	/* Not a CJK font */
675	else			/* a Korean font without CJK Ideographs */
676	    fwstr = FULL_WIDTH_TEST_STRING2;
677    }
678
679    XftTextExtents32(dpy, font, &c1, 1, &gi1);
680    XftTextExtents32(dpy, font, &c2, 1, &gi2);
681    if (gi1.xOff != gi2.xOff)	/* Not a fixed-width font */
682	return False;
683
684    XftTextExtentsUtf8(dpy,
685		       font,
686		       (_Xconst FcChar8 *) hwstr,
687		       (int) strlen(hwstr),
688		       &gi1);
689    XftTextExtentsUtf8(dpy,
690		       font,
691		       (_Xconst FcChar8 *) fwstr,
692		       (int) strlen(fwstr),
693		       &gi2);
694
695    /*
696     * fontconfig and Xft prior to 2.2(?) set the width of half-width
697     * characters identical to that of full-width character in CJK double-width
698     * (bi-width / monospace) font even though the former is half as wide as
699     * the latter.  This was fixed sometime before the release of fontconfig
700     * 2.2 in early 2003.  See
701     *  http://bugzilla.mozilla.org/show_bug.cgi?id=196312
702     * In the meantime, we have to check both possibilities.
703     */
704    return ((2 * gi1.xOff == gi2.xOff) || (gi1.xOff == gi2.xOff));
705}
706#else
707#define is_double_width_font_xft(dpy, xftfont) 0
708#endif
709
710#define EmptyFont(fs) (fs != 0 \
711		   && ((fs)->ascent + (fs)->descent == 0 \
712		    || (fs)->max_bounds.width == 0))
713
714#define FontSize(fs) (((fs)->ascent + (fs)->descent) \
715		    *  (fs)->max_bounds.width)
716
717const VTFontNames *
718xtermFontName(const char *normal)
719{
720    static VTFontNames data;
721    if (data.f_n)
722	free((void *) data.f_n);
723    memset(&data, 0, sizeof(data));
724    data.f_n = x_strdup(normal);
725    return &data;
726}
727
728static void
729cache_menu_font_name(TScreen * screen, int fontnum, int which, const char *name)
730{
731    if (name != 0) {
732	char *last = (char *) screen->menu_font_names[fontnum][which];
733	if (last != 0) {
734	    if (strcmp(last, name)) {
735		free(last);
736		TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
737		screen->menu_font_names[fontnum][which] = x_strdup(name);
738	    }
739	} else {
740	    TRACE(("caching menu fontname %d.%d %s\n", fontnum, which, name));
741	    screen->menu_font_names[fontnum][which] = x_strdup(name);
742	}
743    }
744}
745
746/*
747 * Open the given font and verify that it is non-empty.  Return a null on
748 * failure.
749 */
750Bool
751xtermOpenFont(XtermWidget xw,
752	      const char *name,
753	      XTermFonts * result,
754	      fontWarningTypes warn,
755	      Bool force)
756{
757    Bool code = False;
758    TScreen *screen = TScreenOf(xw);
759
760    if (!IsEmpty(name)) {
761	if ((result->fs = XLoadQueryFont(screen->display, name)) != 0) {
762	    code = True;
763	    if (EmptyFont(result->fs)) {
764		(void) xtermCloseFont(xw, result);
765		code = False;
766	    } else {
767		result->fn = x_strdup(name);
768	    }
769	} else if (XmuCompareISOLatin1(name, DEFFONT) != 0) {
770	    if (warn <= xw->misc.fontWarnings
771#if OPT_RENDERFONT
772		&& !UsingRenderFont(xw)
773#endif
774		) {
775		TRACE(("OOPS: cannot load font %s\n", name));
776		xtermWarning("cannot load font '%s'\n", name);
777#if OPT_RENDERFONT
778		/*
779		 * Do a sanity check in case someone's mixed up xterm with
780		 * one of those programs that read their resource data from
781		 * xterm's namespace.
782		 */
783		if (strchr(name, ':') != 0 || strchr(name, '=') != 0) {
784		    xtermWarning("Use the \"-fa\" option for the Xft fonts\n");
785		}
786#endif
787	    } else {
788		TRACE(("xtermOpenFont: cannot load font '%s'\n", name));
789	    }
790	    if (force) {
791		code = xtermOpenFont(xw, DEFFONT, result, fwAlways, True);
792	    }
793	}
794    }
795    return code;
796}
797
798/*
799 * Close the font and free the font info.
800 */
801XTermFonts *
802xtermCloseFont(XtermWidget xw, XTermFonts * fnt)
803{
804    if (fnt != 0 && fnt->fs != 0) {
805	TScreen *screen = TScreenOf(xw);
806
807	clrCgsFonts(xw, WhichVWin(screen), fnt);
808	XFreeFont(screen->display, fnt->fs);
809	xtermFreeFontInfo(fnt);
810    }
811    return 0;
812}
813
814/*
815 * Close the listed fonts, noting that some may use copies of the pointer.
816 */
817void
818xtermCloseFonts(XtermWidget xw, XTermFonts * fnts)
819{
820    int j, k;
821
822    for (j = 0; j < fMAX; ++j) {
823	/*
824	 * Need to save the pointer since xtermCloseFont zeroes it
825	 */
826	XFontStruct *thisFont = fnts[j].fs;
827	if (thisFont != 0) {
828	    xtermCloseFont(xw, &fnts[j]);
829	    for (k = j + 1; k < fMAX; ++k) {
830		if (thisFont == fnts[k].fs)
831		    xtermFreeFontInfo(&fnts[k]);
832	    }
833	}
834    }
835}
836
837/*
838 * Make a copy of the source, assuming the XFontStruct's to be unique, but
839 * ensuring that the names are reallocated to simplify freeing.
840 */
841void
842xtermCopyFontInfo(XTermFonts * target, XTermFonts * source)
843{
844    xtermFreeFontInfo(target);
845    target->chrset = source->chrset;
846    target->flags = source->flags;
847    target->fn = x_strdup(source->fn);
848    target->fs = source->fs;
849}
850
851void
852xtermFreeFontInfo(XTermFonts * target)
853{
854    target->chrset = 0;
855    target->flags = 0;
856    if (target->fn != 0) {
857	free(target->fn);
858	target->fn = 0;
859    }
860    target->fs = 0;
861}
862
863int
864xtermLoadFont(XtermWidget xw,
865	      const VTFontNames * fonts,
866	      Bool doresize,
867	      int fontnum)
868{
869    TScreen *screen = TScreenOf(xw);
870    VTwin *win = WhichVWin(screen);
871
872    VTFontNames myfonts;
873    FontNameProperties *fp;
874    XTermFonts fnts[fMAX];
875    Pixel new_normal;
876    Pixel new_revers;
877    char *tmpname = NULL;
878    char *normal = NULL;
879    Boolean proportional = False;
880    fontWarningTypes warn[fMAX];
881    int j;
882
883    memset(&myfonts, 0, sizeof(myfonts));
884    memset(fnts, 0, sizeof(fnts));
885
886    if (fonts != 0)
887	myfonts = *fonts;
888    if (!check_fontname(myfonts.f_n))
889	return 0;
890
891    /*
892     * Check the font names against the resource values, to see which were
893     * derived in a previous call.  If so, we'll only warn about those if
894     * the warning level is set to "always".
895     */
896    for (j = 0; j < fMAX; ++j) {
897	warn[j] = fwAlways;
898    }
899#define CmpResource(field, index) \
900    if (same_font_name(screen->menu_font_names[fontnum][index], myfonts.field)) \
901	warn[index] = fwResource
902
903    CmpResource(f_n, fNorm);
904    if (fontnum == fontMenu_default) {
905	CmpResource(f_b, fBold);
906#if OPT_WIDE_CHARS
907	CmpResource(f_b, fWide);
908	CmpResource(f_b, fWBold);
909#endif
910    }
911
912    if (fontnum == fontMenu_fontescape
913	&& myfonts.f_n != screen->MenuFontName(fontnum)) {
914	if ((tmpname = x_strdup(myfonts.f_n)) == 0)
915	    return 0;
916    }
917
918    TRACE(("Begin Cgs - xtermLoadFont(%s)\n", myfonts.f_n));
919    releaseWindowGCs(xw, win);
920
921#define DbgResource(name, field, index) \
922    TRACE(("xtermLoadFont #%d "name" %s%s\n", \
923    	   fontnum, \
924	   (warn[index] == fwResource) ? "*" : " ", \
925	   NonNull(myfonts.field)))
926    DbgResource("normal", f_n, fNorm);
927    DbgResource("bold  ", f_b, fBold);
928#if OPT_WIDE_CHARS
929    DbgResource("wide  ", f_w, fWide);
930    DbgResource("w/bold", f_wb, fWBold);
931#endif
932
933    /*
934     * If we are opening the default font, and it happens to be missing, force
935     * that to the compiled-in default font, e.g., "fixed".  If we cannot open
936     * the font, disable it from the menu.
937     */
938    if (!xtermOpenFont(xw,
939		       myfonts.f_n,
940		       &fnts[fNorm],
941		       warn[fNorm],
942		       (fontnum == fontMenu_default))) {
943	SetItemSensitivity(fontMenuEntries[fontnum].widget, False);
944	goto bad;
945    }
946
947    normal = x_strdup(myfonts.f_n);
948    if (!check_fontname(myfonts.f_b)) {
949	warn[fBold] = fwAlways;
950	fp = get_font_name_props(screen->display, fnts[fNorm].fs, &normal);
951	if (fp != 0) {
952	    myfonts.f_b = bold_font_name(fp, fp->average_width);
953	    if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False)) {
954		myfonts.f_b = bold_font_name(fp, -1);
955		xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False);
956	    }
957	    TRACE(("...derived bold '%s'\n", NonNull(myfonts.f_b)));
958	}
959	if (fp == 0 || fnts[fBold].fs == 0) {
960	    xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
961	    TRACE(("...cannot load a matching bold font\n"));
962	} else if (same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
963		   && got_bold_font(screen->display, fnts[fBold].fs, myfonts.f_b)) {
964	    TRACE(("...got a matching bold font\n"));
965	    cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
966	} else {
967	    xtermCloseFont(xw, &fnts[fBold]);
968	    fnts[fBold] = fnts[fNorm];
969	    TRACE(("...did not get a matching bold font\n"));
970	}
971    } else if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], warn[fBold], False)) {
972	xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
973	warn[fBold] = fwAlways;
974	TRACE(("...cannot load bold font '%s'\n", NonNull(myfonts.f_b)));
975    } else {
976	cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
977    }
978
979    /*
980     * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH
981     * of normal fonts XLFD, and asking for it.  This plucks out 18x18ja
982     * and 12x13ja as the corresponding fonts for 9x18 and 6x13.
983     */
984    if_OPT_WIDE_CHARS(screen, {
985	Boolean derived;
986	char *bold = NULL;
987
988	if (check_fontname(myfonts.f_w)) {
989	    cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
990	} else if (screen->utf8_fonts && !is_double_width_font(fnts[fNorm].fs)) {
991	    fp = get_font_name_props(screen->display, fnts[fNorm].fs, &normal);
992	    if (fp != 0) {
993		myfonts.f_w = wide_font_name(fp);
994		warn[fWide] = fwAlways;
995		TRACE(("...derived wide %s\n", NonNull(myfonts.f_w)));
996		cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
997	    }
998	}
999
1000	if (check_fontname(myfonts.f_w)) {
1001	    (void) xtermOpenFont(xw, myfonts.f_w, &fnts[fWide], warn[fWide], False);
1002	} else {
1003	    xtermCopyFontInfo(&fnts[fWide], &fnts[fNorm]);
1004	    warn[fWide] = fwAlways;
1005	}
1006
1007	derived = False;
1008	if (!check_fontname(myfonts.f_wb)) {
1009	    fp = get_font_name_props(screen->display, fnts[fBold].fs, &bold);
1010	    if (fp != 0) {
1011		myfonts.f_wb = widebold_font_name(fp);
1012		warn[fWBold] = fwAlways;
1013		derived = True;
1014	    }
1015	}
1016
1017	if (check_fontname(myfonts.f_wb)) {
1018
1019	    xtermOpenFont(xw,
1020			  myfonts.f_wb,
1021			  &fnts[fWBold],
1022			  (screen->utf8_fonts
1023			   ? warn[fWBold]
1024			   : (fontWarningTypes) (xw->misc.fontWarnings + 1)),
1025			  False);
1026
1027	    if (derived
1028		&& !compatibleWideCounts(fnts[fWide].fs, fnts[fWBold].fs)) {
1029		xtermCloseFont(xw, &fnts[fWBold]);
1030	    }
1031	    if (fnts[fWBold].fs == 0) {
1032		if (IsEmpty(myfonts.f_w)) {
1033		    myfonts.f_wb = myfonts.f_b;
1034		    warn[fWBold] = fwAlways;
1035		    xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
1036		    TRACE(("...cannot load wide-bold, use bold %s\n",
1037			   NonNull(myfonts.f_b)));
1038		} else {
1039		    myfonts.f_wb = myfonts.f_w;
1040		    warn[fWBold] = fwAlways;
1041		    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1042		    TRACE(("...cannot load wide-bold, use wide %s\n",
1043			   NonNull(myfonts.f_w)));
1044		}
1045	    } else {
1046		TRACE(("...%s wide/bold %s\n",
1047		       derived ? "derived" : "given",
1048		       NonNull(myfonts.f_wb)));
1049		cache_menu_font_name(screen, fontnum, fWBold, myfonts.f_wb);
1050	    }
1051	} else if (is_double_width_font(fnts[fBold].fs)) {
1052	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
1053	    warn[fWBold] = fwAlways;
1054	    TRACE(("...bold font is double-width, use it %s\n", NonNull(myfonts.f_b)));
1055	} else {
1056	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1057	    warn[fWBold] = fwAlways;
1058	    TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(myfonts.f_w)));
1059	}
1060
1061	free(bold);
1062
1063	if (EmptyFont(fnts[fWBold].fs))
1064	    goto bad;		/* can't use a 0-sized font */
1065    });
1066
1067    /*
1068     * Most of the time this call to load the font will succeed, even if
1069     * there is no wide font :  the X server doubles the width of the
1070     * normal font, or similar.
1071     *
1072     * But if it did fail for some reason, then nevermind.
1073     */
1074    if (EmptyFont(fnts[fBold].fs))
1075	goto bad;		/* can't use a 0-sized font */
1076
1077    if (!same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
1078	&& (is_fixed_font(fnts[fNorm].fs) && is_fixed_font(fnts[fBold].fs))) {
1079	TRACE(("...ignoring mismatched normal/bold fonts\n"));
1080	xtermCloseFont(xw, &fnts[fBold]);
1081	xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
1082    }
1083
1084    if_OPT_WIDE_CHARS(screen, {
1085	if (fnts[fWide].fs != 0
1086	    && fnts[fWBold].fs != 0
1087	    && !same_font_size(xw, fnts[fWide].fs, fnts[fWBold].fs)
1088	    && (is_fixed_font(fnts[fWide].fs) && is_fixed_font(fnts[fWBold].fs))) {
1089	    TRACE(("...ignoring mismatched normal/bold wide fonts\n"));
1090	    xtermCloseFont(xw, &fnts[fWBold]);
1091	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1092	}
1093    });
1094
1095    /*
1096     * Normal/bold fonts should be the same width.  Also, the min/max
1097     * values should be the same.
1098     */
1099    if (!is_fixed_font(fnts[fNorm].fs)
1100	|| !is_fixed_font(fnts[fBold].fs)
1101	|| fnts[fNorm].fs->max_bounds.width != fnts[fBold].fs->max_bounds.width) {
1102	TRACE(("Proportional font! normal %d/%d, bold %d/%d\n",
1103	       fnts[fNorm].fs->min_bounds.width,
1104	       fnts[fNorm].fs->max_bounds.width,
1105	       fnts[fBold].fs->min_bounds.width,
1106	       fnts[fBold].fs->max_bounds.width));
1107	proportional = True;
1108    }
1109
1110    if_OPT_WIDE_CHARS(screen, {
1111	if (fnts[fWide].fs != 0
1112	    && fnts[fWBold].fs != 0
1113	    && (!is_fixed_font(fnts[fWide].fs)
1114		|| !is_fixed_font(fnts[fWBold].fs)
1115		|| fnts[fWide].fs->max_bounds.width != fnts[fWBold].fs->max_bounds.width)) {
1116	    TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n",
1117		   fnts[fWide].fs->min_bounds.width,
1118		   fnts[fWide].fs->max_bounds.width,
1119		   fnts[fWBold].fs->min_bounds.width,
1120		   fnts[fWBold].fs->max_bounds.width));
1121	    proportional = True;
1122	}
1123    });
1124
1125    /* TODO : enforce that the width of the wide font is 2* the width
1126       of the narrow font */
1127
1128    /*
1129     * If we're switching fonts, free the old ones.  Otherwise we'll leak
1130     * the memory that is associated with the old fonts.  The
1131     * XLoadQueryFont call allocates a new XFontStruct.
1132     */
1133    xtermCloseFonts(xw, screen->fnts);
1134
1135    xtermCopyFontInfo(&(screen->fnts[fNorm]), &fnts[fNorm]);
1136    xtermCopyFontInfo(&(screen->fnts[fBold]), &fnts[fBold]);
1137#if OPT_WIDE_CHARS
1138    xtermCopyFontInfo(&(screen->fnts[fWide]), &fnts[fWide]);
1139    if (fnts[fWBold].fs == NULL)
1140	xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1141    xtermCopyFontInfo(&(screen->fnts[fWBold]), &fnts[fWBold]);
1142#endif
1143
1144    new_normal = getXtermForeground(xw, xw->flags, xw->cur_foreground);
1145    new_revers = getXtermBackground(xw, xw->flags, xw->cur_background);
1146
1147    setCgsFore(xw, win, gcNorm, new_normal);
1148    setCgsBack(xw, win, gcNorm, new_revers);
1149    setCgsFont(xw, win, gcNorm, &(screen->fnts[fNorm]));
1150
1151    copyCgs(xw, win, gcBold, gcNorm);
1152    setCgsFont(xw, win, gcBold, &(screen->fnts[fBold]));
1153
1154    setCgsFore(xw, win, gcNormReverse, new_revers);
1155    setCgsBack(xw, win, gcNormReverse, new_normal);
1156    setCgsFont(xw, win, gcNormReverse, &(screen->fnts[fNorm]));
1157
1158    copyCgs(xw, win, gcBoldReverse, gcNormReverse);
1159    setCgsFont(xw, win, gcBoldReverse, &(screen->fnts[fBold]));
1160
1161    if_OPT_WIDE_CHARS(screen, {
1162	if (screen->fnts[fWide].fs != 0
1163	    && screen->fnts[fWBold].fs != 0) {
1164	    setCgsFore(xw, win, gcWide, new_normal);
1165	    setCgsBack(xw, win, gcWide, new_revers);
1166	    setCgsFont(xw, win, gcWide, &(screen->fnts[fWide]));
1167
1168	    copyCgs(xw, win, gcWBold, gcWide);
1169	    setCgsFont(xw, win, gcWBold, &(screen->fnts[fWBold]));
1170
1171	    setCgsFore(xw, win, gcWideReverse, new_revers);
1172	    setCgsBack(xw, win, gcWideReverse, new_normal);
1173	    setCgsFont(xw, win, gcWideReverse, &(screen->fnts[fWide]));
1174
1175	    copyCgs(xw, win, gcWBoldReverse, gcWideReverse);
1176	    setCgsFont(xw, win, gcWBoldReverse, &(screen->fnts[fWBold]));
1177	}
1178    });
1179
1180#if OPT_BOX_CHARS
1181    screen->allow_packing = proportional;
1182    setupPackedFonts(xw);
1183#endif
1184    screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed));
1185    screen->fnt_boxes = True;
1186
1187#if OPT_BOX_CHARS
1188    /*
1189     * Xterm uses character positions 1-31 of a font for the line-drawing
1190     * characters.  Check that they are all present.  The null character
1191     * (0) is special, and is not used.
1192     */
1193#if OPT_RENDERFONT
1194    if (UsingRenderFont(xw)) {
1195	/*
1196	 * FIXME: we shouldn't even be here if we're using Xft.
1197	 */
1198	screen->fnt_boxes = False;
1199	TRACE(("assume Xft missing line-drawing chars\n"));
1200    } else
1201#endif
1202    {
1203	unsigned ch;
1204
1205	for (ch = 1; ch < 32; ch++) {
1206	    unsigned n = ch;
1207#if OPT_WIDE_CHARS
1208	    if (screen->utf8_mode || screen->unicode_font) {
1209		n = dec2ucs(ch);
1210		if (n == UCS_REPL)
1211		    continue;
1212	    }
1213#endif
1214	    if (IsXtermMissingChar(screen, n, &fnts[fNorm])) {
1215		TRACE(("missing normal char #%d\n", n));
1216		screen->fnt_boxes = False;
1217		break;
1218	    }
1219	    if (IsXtermMissingChar(screen, n, &fnts[fBold])) {
1220		TRACE(("missing bold char #%d\n", n));
1221		screen->fnt_boxes = False;
1222		break;
1223	    }
1224	}
1225    }
1226    TRACE(("Will %suse internal line-drawing characters\n",
1227	   screen->fnt_boxes ? "not " : ""));
1228#endif
1229
1230    if (screen->always_bold_mode) {
1231	screen->enbolden = screen->bold_mode;
1232    } else {
1233	screen->enbolden = screen->bold_mode
1234	    && ((fnts[fNorm].fs == fnts[fBold].fs)
1235		|| same_font_name(normal, myfonts.f_b));
1236    }
1237    TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n",
1238	   screen->enbolden ? "" : "not "));
1239
1240    set_menu_font(False);
1241    screen->menu_font_number = fontnum;
1242    set_menu_font(True);
1243    if (tmpname) {		/* if setting escape or sel */
1244	if (screen->MenuFontName(fontnum))
1245	    free((void *) screen->MenuFontName(fontnum));
1246	screen->MenuFontName(fontnum) = tmpname;
1247	if (fontnum == fontMenu_fontescape) {
1248	    SetItemSensitivity(fontMenuEntries[fontMenu_fontescape].widget,
1249			       True);
1250	}
1251#if OPT_SHIFT_FONTS
1252	screen->menu_font_sizes[fontnum] = FontSize(fnts[fNorm].fs);
1253#endif
1254    }
1255    if (normal)
1256	free(normal);
1257    set_cursor_gcs(xw);
1258    xtermUpdateFontInfo(xw, doresize);
1259    TRACE(("Success Cgs - xtermLoadFont\n"));
1260    return 1;
1261
1262  bad:
1263    if (normal)
1264	free(normal);
1265    if (tmpname)
1266	free(tmpname);
1267
1268#if OPT_RENDERFONT
1269    if (x_strcasecmp(myfonts.f_n, DEFFONT)) {
1270	int code;
1271
1272	myfonts.f_n = DEFFONT;
1273	TRACE(("...recovering for TrueType fonts\n"));
1274	code = xtermLoadFont(xw, &myfonts, doresize, fontnum);
1275	if (code) {
1276	    SetItemSensitivity(fontMenuEntries[fontnum].widget,
1277			       UsingRenderFont(xw));
1278	    TRACE(("...recovered size %dx%d\n",
1279		   FontHeight(screen),
1280		   FontWidth(screen)));
1281	}
1282	return code;
1283    }
1284#endif
1285
1286    releaseWindowGCs(xw, win);
1287
1288    xtermCloseFonts(xw, fnts);
1289    TRACE(("Fail Cgs - xtermLoadFont\n"));
1290    return 0;
1291}
1292
1293#if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
1294/*
1295 * Collect font-names that we can modify with the load-vt-fonts() action.
1296 */
1297#define MERGE_SUBFONT(src,dst,name) \
1298	if (IsEmpty(dst.name)) { \
1299	    TRACE(("MERGE_SUBFONT " #dst "." #name " merge %s\n", NonNull(src.name))); \
1300	    dst.name = src.name; \
1301	} else { \
1302	    TRACE(("MERGE_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
1303	}
1304
1305#define INFER_SUBFONT(src,dst,name) \
1306	if (IsEmpty(dst.name)) { \
1307	    TRACE(("INFER_SUBFONT " #dst "." #name " will infer\n")); \
1308	    dst.name = x_strdup(""); \
1309	} else { \
1310	    TRACE(("INFER_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
1311	}
1312
1313#define COPY_MENU_FONTS(src,dst) \
1314	TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \
1315	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
1316	    for (m = 0; m < fMAX; ++m) { \
1317		dst.menu_font_names[n][m] = x_strdup(src.menu_font_names[n][m]); \
1318	    } \
1319	    TRACE((".. " #dst ".menu_fonts_names[%d] = %s\n", n, dst.menu_font_names[n][fNorm])); \
1320	}
1321
1322void
1323xtermSaveVTFonts(XtermWidget xw)
1324{
1325    TScreen *screen = TScreenOf(xw);
1326    Cardinal n, m;
1327
1328    if (!screen->savedVTFonts) {
1329
1330	screen->savedVTFonts = True;
1331	TRACE(("xtermSaveVTFonts saving original\n"));
1332	screen->cacheVTFonts.default_font = xw->misc.default_font;
1333	COPY_MENU_FONTS(xw->screen, screen->cacheVTFonts);
1334    }
1335}
1336
1337#define SAME_STRING(x,y) ((x) == (y) || ((x) && (y) && !strcmp(x, y)))
1338#define SAME_MEMBER(n)   SAME_STRING(a->n, b->n)
1339
1340static Boolean
1341sameSubResources(SubResourceRec * a, SubResourceRec * b)
1342{
1343    Boolean result = True;
1344    int n;
1345
1346    if (!SAME_MEMBER(default_font.f_n)
1347	|| !SAME_MEMBER(default_font.f_b)
1348#if OPT_WIDE_CHARS
1349	|| !SAME_MEMBER(default_font.f_w)
1350	|| !SAME_MEMBER(default_font.f_wb)
1351#endif
1352	) {
1353	TRACE(("sameSubResources: default_font differs\n"));
1354	result = False;
1355    } else {
1356	for (n = 0; n < NMENUFONTS; ++n) {
1357	    if (!SAME_MEMBER(menu_font_names[n][fNorm])) {
1358		TRACE(("sameSubResources: menu_font_names[%d] differs\n", n));
1359		result = False;
1360		break;
1361	    }
1362	}
1363    }
1364
1365    return result;
1366}
1367
1368/*
1369 * Load the "VT" font names from the given subresource name/class.  These
1370 * correspond to the VT100 resources.
1371 */
1372static Bool
1373xtermLoadVTFonts(XtermWidget xw, String myName, String myClass)
1374{
1375    SubResourceRec subresourceRec;
1376    SubResourceRec referenceRec;
1377
1378    /*
1379     * These are duplicates of the VT100 font resources, but with a special
1380     * application/classname passed in to distinguish them.
1381     */
1382    static XtResource font_resources[] =
1383    {
1384	Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT),
1385	Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT),
1386#if OPT_WIDE_CHARS
1387	Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT),
1388	Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT),
1389#endif
1390	Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL),
1391	Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL),
1392	Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL),
1393	Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL),
1394	Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL),
1395	Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL),
1396    };
1397    Cardinal n, m;
1398    Bool status = True;
1399    TScreen *screen = TScreenOf(xw);
1400
1401    TRACE(("called xtermLoadVTFonts(name=%s, class=%s)\n",
1402	   NonNull(myName), NonNull(myClass)));
1403
1404    xtermSaveVTFonts(xw);
1405
1406    if (IsEmpty(myName)) {
1407	TRACE(("xtermLoadVTFonts restoring original\n"));
1408	xw->misc.default_font = screen->cacheVTFonts.default_font;
1409	COPY_MENU_FONTS(screen->cacheVTFonts, xw->screen);
1410	for (n = 0; n < XtNumber(screen->cacheVTFonts.menu_font_names); ++n) {
1411	    screen->MenuFontName(n) = screen->cacheVTFonts.MenuFontName(n);
1412	}
1413    } else {
1414	TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass));
1415
1416	memset(&referenceRec, 0, sizeof(referenceRec));
1417	memset(&subresourceRec, 0, sizeof(subresourceRec));
1418	XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec,
1419			  myName, myClass,
1420			  font_resources,
1421			  (Cardinal) XtNumber(font_resources),
1422			  NULL, (Cardinal) 0);
1423
1424	/*
1425	 * XtGetSubresources returns no status, so we compare the returned
1426	 * data against a zero'd struct to see if any data is returned.
1427	 */
1428	if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec))
1429	    && !sameSubResources(&(screen->cacheVTFonts), &subresourceRec)) {
1430
1431	    screen->mergedVTFonts = True;
1432
1433	    /*
1434	     * If a particular resource value was not found, use the original.
1435	     */
1436	    MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_n);
1437	    INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_b);
1438#if OPT_WIDE_CHARS
1439	    INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_w);
1440	    INFER_SUBFONT(xw->misc, subresourceRec, default_font.f_wb);
1441#endif
1442	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n)
1443		MERGE_SUBFONT(xw->screen, subresourceRec, MenuFontName(n));
1444
1445	    /*
1446	     * Finally, copy the subresource data to the widget.
1447	     */
1448	    xw->misc.default_font = subresourceRec.default_font;
1449	    COPY_MENU_FONTS(subresourceRec, xw->screen);
1450	    screen->MenuFontName(fontMenu_default) = x_strdup(xw->misc.default_font.f_n);
1451	    screen->menu_font_names[0][fBold] = x_strdup(xw->misc.default_font.f_b);
1452#if OPT_WIDE_CHARS
1453	    screen->menu_font_names[0][fWide] = x_strdup(xw->misc.default_font.f_w);
1454	    screen->menu_font_names[0][fWBold] = x_strdup(xw->misc.default_font.f_wb);
1455#endif
1456	} else {
1457	    TRACE(("...no resources found\n"));
1458	    status = False;
1459	}
1460    }
1461    return status;
1462}
1463
1464#if OPT_WIDE_CHARS
1465static Bool
1466isWideFont(XFontStruct * fp, const char *tag, Bool nullOk)
1467{
1468    Bool result = False;
1469
1470    (void) tag;
1471    if (okFont(fp)) {
1472	unsigned count = countGlyphs(fp);
1473	TRACE(("isWideFont(%s) found %d cells\n", tag, count));
1474	result = (count > 256) ? True : False;
1475    } else {
1476	result = nullOk;
1477    }
1478    return result;
1479}
1480
1481/*
1482 * If the current fonts are not wide, load the UTF8 fonts.
1483 *
1484 * Called during initialization (for wide-character mode), the fonts have not
1485 * been setup, so we pass nullOk=True to isWideFont().
1486 *
1487 * Called after initialization, e.g., in response to the UTF-8 menu entry
1488 * (starting from narrow character mode), it checks if the fonts are not wide.
1489 */
1490Bool
1491xtermLoadWideFonts(XtermWidget xw, Bool nullOk)
1492{
1493    TScreen *screen = TScreenOf(xw);
1494    Bool result;
1495
1496    if (EmptyFont(screen->fnts[fWide].fs)) {
1497	result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
1498		  && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
1499    } else {
1500	result = (isWideFont(screen->fnts[fWide].fs, "wide", nullOk)
1501		  && isWideFont(screen->fnts[fWBold].fs, "wide-bold", nullOk));
1502	if (result && !screen->utf8_latin1) {
1503	    result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
1504		      && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
1505	}
1506    }
1507    if (!result) {
1508	TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : ""));
1509	result = xtermLoadVTFonts(xw, XtNutf8Fonts, XtCUtf8Fonts);
1510    }
1511    TRACE(("xtermLoadWideFonts:%d\n", result));
1512    return result;
1513}
1514#endif /* OPT_WIDE_CHARS */
1515
1516/*
1517 * Restore the default fonts, i.e., if we had switched to wide-fonts.
1518 */
1519Bool
1520xtermLoadDefaultFonts(XtermWidget xw)
1521{
1522    Bool result;
1523    result = xtermLoadVTFonts(xw, NULL, NULL);
1524    TRACE(("xtermLoadDefaultFonts:%d\n", result));
1525    return result;
1526}
1527#endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */
1528
1529#if OPT_LOAD_VTFONTS
1530void
1531HandleLoadVTFonts(Widget w,
1532		  XEvent * event GCC_UNUSED,
1533		  String * params GCC_UNUSED,
1534		  Cardinal *param_count GCC_UNUSED)
1535{
1536    static char empty[] = "";	/* appease strict compilers */
1537
1538    XtermWidget xw;
1539
1540    if ((xw = getXtermWidget(w)) != 0) {
1541	TScreen *screen = TScreenOf(xw);
1542	char name_buf[80];
1543	char class_buf[80];
1544	String name = (String) ((*param_count > 0) ? params[0] : empty);
1545	char *myName = (char *) MyStackAlloc(strlen(name) + 1, name_buf);
1546	String convert = (String) ((*param_count > 1) ? params[1] : myName);
1547	char *myClass = (char *) MyStackAlloc(strlen(convert) + 1, class_buf);
1548	int n;
1549
1550	TRACE(("HandleLoadVTFonts(%d)\n", *param_count));
1551	strcpy(myName, name);
1552	strcpy(myClass, convert);
1553	if (*param_count == 1)
1554	    myClass[0] = x_toupper(myClass[0]);
1555
1556	if (xtermLoadVTFonts(xw, myName, myClass)) {
1557	    /*
1558	     * When switching fonts, try to preserve the font-menu selection, since
1559	     * it is less surprising to do that (if the font-switching can be
1560	     * undone) than to switch to "Default".
1561	     */
1562	    int font_number = screen->menu_font_number;
1563	    if (font_number > fontMenu_lastBuiltin)
1564		font_number = fontMenu_lastBuiltin;
1565	    for (n = 0; n < NMENUFONTS; ++n)
1566		screen->menu_font_sizes[n] = 0;
1567	    SetVTFont(xw, font_number, True,
1568		      ((font_number == fontMenu_default)
1569		       ? &(xw->misc.default_font)
1570		       : NULL));
1571	}
1572
1573	MyStackFree(myName, name_buf);
1574	MyStackFree(myClass, class_buf);
1575    }
1576}
1577#endif /* OPT_LOAD_VTFONTS */
1578
1579/*
1580 * Set the limits for the box that outlines the cursor.
1581 */
1582void
1583xtermSetCursorBox(TScreen * screen)
1584{
1585    static XPoint VTbox[NBOX];
1586    XPoint *vp;
1587    int fw = FontWidth(screen) - 1;
1588    int fh = FontHeight(screen) - 1;
1589    int ww = isCursorBar(screen) ? 1 : fw;
1590    int hh = isCursorUnderline(screen) ? 1 : fh;
1591
1592    vp = &VTbox[1];
1593    (vp++)->x = (short) ww;
1594    (vp++)->y = (short) hh;
1595    (vp++)->x = (short) -ww;
1596    vp->y = (short) -hh;
1597
1598    screen->box = VTbox;
1599}
1600
1601#define CACHE_XFT(dst,src) if (src != 0) {\
1602	    checkXft(xw, &(dst[fontnum]), src);\
1603	    TRACE(("Xft metrics %s[%d] = %d (%d,%d)%s advance %d, actual %d%s\n",\
1604		#dst,\
1605	    	fontnum,\
1606		src->height,\
1607		src->ascent,\
1608		src->descent,\
1609		((src->ascent + src->descent) > src->height ? "*" : ""),\
1610		src->max_advance_width,\
1611		dst[fontnum].map.min_width,\
1612		dst[fontnum].map.mixed ? " mixed" : ""));\
1613	}
1614
1615#if OPT_RENDERFONT
1616
1617#if OPT_TRACE > 1
1618static FcChar32
1619xtermXftFirstChar(XftFont * xft)
1620{
1621    FcChar32 map[FC_CHARSET_MAP_SIZE];
1622    FcChar32 next;
1623    FcChar32 first;
1624    int i;
1625
1626    first = FcCharSetFirstPage(xft->charset, map, &next);
1627    for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
1628	if (map[i]) {
1629	    FcChar32 bits = map[i];
1630	    first += i * 32;
1631	    while (!(bits & 0x1)) {
1632		bits >>= 1;
1633		first++;
1634	    }
1635	    break;
1636	}
1637    return first;
1638}
1639
1640static FcChar32
1641xtermXftLastChar(XftFont * xft)
1642{
1643    FcChar32 this, last, next;
1644    FcChar32 map[FC_CHARSET_MAP_SIZE];
1645    int i;
1646    last = FcCharSetFirstPage(xft->charset, map, &next);
1647    while ((this = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE)
1648	last = this;
1649    last &= ~0xff;
1650    for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
1651	if (map[i]) {
1652	    FcChar32 bits = map[i];
1653	    last += i * 32 + 31;
1654	    while (!(bits & 0x80000000)) {
1655		last--;
1656		bits <<= 1;
1657	    }
1658	    break;
1659	}
1660    return (long) last;
1661}
1662
1663static void
1664dumpXft(XtermWidget xw, XTermXftFonts * data)
1665{
1666    XftFont *xft = data->font;
1667    TScreen *screen = TScreenOf(xw);
1668    VTwin *win = WhichVWin(screen);
1669
1670    FcChar32 c;
1671    FcChar32 first = xtermXftFirstChar(xft);
1672    FcChar32 last = xtermXftLastChar(xft);
1673    unsigned count = 0;
1674    unsigned outside = 0;
1675
1676    TRACE(("dumpXft {{\n"));
1677    TRACE(("   data range %#6x..%#6x\n", first, last));
1678    for (c = first; c <= last; ++c) {
1679	if (FcCharSetHasChar(xft->charset, c)) {
1680	    int width = my_wcwidth((int) c);
1681	    XGlyphInfo extents;
1682
1683	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
1684	    TRACE(("%#6x  %2d  %.1f\n", c, width,
1685		   ((double) extents.width) / win->f_width));
1686	    if (extents.width > win->f_width)
1687		++outside;
1688	    ++count;
1689	}
1690    }
1691    TRACE(("}} %u total, %u outside\n", count, outside));
1692}
1693#define DUMP_XFT(xw, data) dumpXft(xw, data)
1694#else
1695#define DUMP_XFT(xw, data)	/* nothing */
1696#endif
1697
1698static void
1699checkXft(XtermWidget xw, XTermXftFonts * data, XftFont * xft)
1700{
1701    FcChar32 c;
1702    Dimension width = 0;
1703
1704    data->font = xft;
1705    data->map.min_width = 0;
1706    data->map.max_width = (Dimension) xft->max_advance_width;
1707
1708    /*
1709     * For each ASCII or ISO-8859-1 printable code, ask what its width is.
1710     * Given the maximum width for those, we have a reasonable estimate of
1711     * the single-column width.
1712     *
1713     * Ignore control characters - their extent information is misleading.
1714     */
1715    for (c = 32; c < 256; ++c) {
1716	if (c >= 127 && c <= 159)
1717	    continue;
1718	if (FcCharSetHasChar(xft->charset, c)) {
1719	    XGlyphInfo extents;
1720
1721	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
1722	    if (width < extents.width && extents.width <= data->map.max_width) {
1723		width = extents.width;
1724	    }
1725	}
1726    }
1727    data->map.min_width = width;
1728    data->map.mixed = (data->map.max_width >= (data->map.min_width + 1));
1729}
1730
1731static XftFont *
1732xtermOpenXft(XtermWidget xw, const char *name, XftPattern * pat, const char *tag)
1733{
1734    TScreen *screen = TScreenOf(xw);
1735    Display *dpy = screen->display;
1736    XftPattern *match;
1737    XftResult status;
1738    XftFont *result = 0;
1739
1740    if (pat != 0) {
1741	match = XftFontMatch(dpy, DefaultScreen(dpy), pat, &status);
1742	if (match != 0) {
1743	    result = XftFontOpenPattern(dpy, match);
1744	    if (result != 0) {
1745		TRACE(("...matched %s font\n", tag));
1746	    } else {
1747		TRACE(("...could did not open %s font\n", tag));
1748		XftPatternDestroy(match);
1749		if (xw->misc.fontWarnings >= fwAlways) {
1750		    TRACE(("OOPS cannot open %s font \"%s\"\n", tag, name));
1751		    xtermWarning("cannot open %s font \"%s\"\n", tag, name);
1752		}
1753	    }
1754	} else {
1755	    TRACE(("...did not match %s font\n", tag));
1756	    if (xw->misc.fontWarnings >= fwResource) {
1757		TRACE(("OOPS: cannot match %s font \"%s\"\n", tag, name));
1758		xtermWarning("cannot match %s font \"%s\"\n", tag, name);
1759	    }
1760	}
1761    }
1762    return result;
1763}
1764#endif
1765
1766#if OPT_RENDERFONT
1767#if OPT_SHIFT_FONTS
1768/*
1769 * Don't make a dependency on the math library for a single function.
1770 * (Newton Raphson).
1771 */
1772static double
1773dimSquareRoot(double value)
1774{
1775    double result = 0.0;
1776    if (value > 0.0) {
1777	int n;
1778	double older = value;
1779	for (n = 0; n < 10; ++n) {
1780	    double delta = (older * older - value) / (2.0 * older);
1781	    double newer = older - delta;
1782	    older = newer;
1783	    result = newer;
1784	    if (delta > -0.001 && delta < 0.001)
1785		break;
1786	}
1787    }
1788    return result;
1789}
1790#endif
1791
1792/*
1793 * Given the Xft font metrics, determine the actual font size.  This is used
1794 * for each font to ensure that normal, bold and italic fonts follow the same
1795 * rule.
1796 */
1797static void
1798setRenderFontsize(TScreen * screen, VTwin * win, XftFont * font, const char *tag)
1799{
1800    if (font != 0) {
1801	int width, height, ascent, descent;
1802
1803	(void) screen;
1804
1805	width = font->max_advance_width;
1806	height = font->height;
1807	ascent = font->ascent;
1808	descent = font->descent;
1809	if (height < ascent + descent) {
1810	    TRACE(("...increase height from %d\n", height));
1811	    height = ascent + descent;
1812	}
1813	if (is_double_width_font_xft(screen->display, font)) {
1814	    TRACE(("...reduced width from %d\n", width));
1815	    width >>= 1;
1816	}
1817	if (tag == 0) {
1818	    SetFontWidth(screen, win, width);
1819	    SetFontHeight(screen, win, height);
1820	    win->f_ascent = ascent;
1821	    win->f_descent = descent;
1822	    TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
1823		   width, height, ascent, descent));
1824	} else if (win->f_width < width ||
1825		   win->f_height < height ||
1826		   win->f_ascent < ascent ||
1827		   win->f_descent < descent) {
1828	    TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
1829		   tag,
1830		   win->f_width, win->f_height, win->f_ascent, win->f_descent,
1831		   width, height, ascent, descent));
1832
1833	    SetFontWidth(screen, win, width);
1834	    SetFontHeight(screen, win, height);
1835	    win->f_ascent = ascent;
1836	    win->f_descent = descent;
1837	} else {
1838	    TRACE(("setRenderFontsize %s unchanged\n", tag));
1839	}
1840    }
1841}
1842#endif
1843
1844static void
1845checkFontInfo(int value, const char *tag)
1846{
1847    if (value == 0) {
1848	xtermWarning("Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag);
1849	exit(1);
1850    }
1851}
1852
1853#if OPT_RENDERFONT
1854void
1855xtermCloseXft(TScreen * screen, XTermXftFonts * pub)
1856{
1857    if (pub->font != 0) {
1858	XftFontClose(screen->display, pub->font);
1859	pub->font = 0;
1860    }
1861}
1862
1863/*
1864 * Get the faceName/faceDoublesize resource setting.  Strip off "xft:", which
1865 * is not recognized by XftNameParse().
1866 */
1867String
1868getFaceName(XtermWidget xw, Bool wideName GCC_UNUSED)
1869{
1870#if OPT_RENDERWIDE
1871    String result = (wideName
1872		     ? xw->misc.face_wide_name
1873		     : xw->misc.face_name);
1874#else
1875    String result = xw->misc.face_name;
1876#endif
1877    if (!IsEmpty(result) && !strncmp(result, "xft:", (size_t) 4))
1878	result += 4;
1879    return x_nonempty(result);
1880}
1881
1882/*
1883 * If we change the faceName, we'll have to re-acquire all of the fonts that
1884 * are derived from it.
1885 */
1886void
1887setFaceName(XtermWidget xw, const char *value)
1888{
1889    TScreen *screen = TScreenOf(xw);
1890    int n;
1891    Boolean changed = (Boolean) ((xw->misc.face_name == 0)
1892				 || strcmp(xw->misc.face_name, value));
1893
1894    if (changed) {
1895	xw->misc.face_name = x_strdup(value);
1896	for (n = 0; n < NMENUFONTS; ++n) {
1897	    xw->misc.face_size[n] = -1.0;
1898	    xtermCloseXft(screen, &(screen->renderFontNorm[n]));
1899	    xtermCloseXft(screen, &(screen->renderFontBold[n]));
1900	    xtermCloseXft(screen, &(screen->renderFontBold[n]));
1901#if OPT_RENDERWIDE
1902	    xtermCloseXft(screen, &(screen->renderWideNorm[n]));
1903	    xtermCloseXft(screen, &(screen->renderWideBold[n]));
1904	    xtermCloseXft(screen, &(screen->renderWideItal[n]));
1905#endif
1906	}
1907    }
1908}
1909#endif
1910
1911/*
1912 * Compute useful values for the font/window sizes
1913 */
1914void
1915xtermComputeFontInfo(XtermWidget xw,
1916		     VTwin * win,
1917		     XFontStruct * font,
1918		     int sbwidth)
1919{
1920    TScreen *screen = TScreenOf(xw);
1921
1922    int i, j, width, height;
1923#if OPT_RENDERFONT
1924    int fontnum = screen->menu_font_number;
1925#endif
1926
1927#if OPT_RENDERFONT
1928    /*
1929     * xterm contains a lot of references to fonts, assuming they are fixed
1930     * size.  This chunk of code overrides the actual font-selection (see
1931     * drawXtermText()), if the user has selected render-font.  All of the
1932     * font-loading for fixed-fonts still goes on whether or not this chunk
1933     * overrides it.
1934     */
1935    if (UsingRenderFont(xw) && fontnum >= 0) {
1936	String face_name = getFaceName(xw, False);
1937	XftFont *norm = screen->renderFontNorm[fontnum].font;
1938	XftFont *bold = screen->renderFontBold[fontnum].font;
1939	XftFont *ital = screen->renderFontItal[fontnum].font;
1940#if OPT_RENDERWIDE
1941	XftFont *wnorm = screen->renderWideNorm[fontnum].font;
1942	XftFont *wbold = screen->renderWideBold[fontnum].font;
1943	XftFont *wital = screen->renderWideItal[fontnum].font;
1944#endif
1945
1946	if (norm == 0 && face_name) {
1947	    XftPattern *pat;
1948	    double face_size;
1949
1950	    TRACE(("xtermComputeFontInfo font %d: norm(face %s, size %.1f)\n",
1951		   fontnum, face_name,
1952		   xw->misc.face_size[fontnum]));
1953
1954	    fillInFaceSize(xw, fontnum);
1955	    face_size = xw->misc.face_size[fontnum];
1956
1957	    /*
1958	     * By observation (there is no documentation), XftPatternBuild is
1959	     * cumulative.  Build the bold- and italic-patterns on top of the
1960	     * normal pattern.
1961	     */
1962#define NormXftPattern \
1963	    XFT_FAMILY, XftTypeString, "mono", \
1964	    XFT_SIZE, XftTypeDouble, face_size, \
1965	    XFT_SPACING, XftTypeInteger, XFT_MONO
1966
1967#define BoldXftPattern(norm) \
1968	    XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD, \
1969	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
1970
1971#define ItalXftPattern(norm) \
1972	    XFT_SLANT, XftTypeInteger, XFT_SLANT_ITALIC, \
1973	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
1974
1975	    if ((pat = XftNameParse(face_name)) != 0) {
1976#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
1977		XftPatternBuild(pat,
1978				NormXftPattern,
1979				(void *) 0);
1980		norm = OPEN_XFT("normal");
1981
1982		if (norm != 0) {
1983		    XftPatternBuild(pat,
1984				    BoldXftPattern(norm),
1985				    (void *) 0);
1986		    bold = OPEN_XFT("bold");
1987
1988#if OPT_ISO_COLORS
1989		    if (screen->italicULMode
1990			&& (pat = XftNameParse(face_name)) != 0) {
1991			XftPatternBuild(pat,
1992					NormXftPattern,
1993					ItalXftPattern(norm),
1994					(void *) 0);
1995			ital = OPEN_XFT("italic");
1996		    }
1997#endif /* OPT_ISO_COLORS */
1998#undef OPEN_XFT
1999
2000		    /*
2001		     * FIXME:  just assume that the corresponding font has no
2002		     * graphics characters.
2003		     */
2004		    if (screen->fnt_boxes) {
2005			screen->fnt_boxes = False;
2006			TRACE(("Xft opened - will %suse internal line-drawing characters\n",
2007			       screen->fnt_boxes ? "not " : ""));
2008		    }
2009		}
2010
2011		XftPatternDestroy(pat);
2012	    }
2013
2014	    CACHE_XFT(screen->renderFontNorm, norm);
2015	    CACHE_XFT(screen->renderFontBold, bold);
2016	    CACHE_XFT(screen->renderFontItal, ital);
2017
2018	    /*
2019	     * See xtermXftDrawString().
2020	     */
2021#if OPT_RENDERWIDE
2022	    if (norm != 0 && screen->wide_chars) {
2023		int char_width = norm->max_advance_width * 2;
2024#ifdef FC_ASPECT
2025		double aspect = ((xw->misc.face_wide_name
2026				  || screen->renderFontNorm[fontnum].map.mixed)
2027				 ? 1.0
2028				 : 2.0);
2029#endif
2030
2031		face_name = getFaceName(xw, True);
2032		TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
2033		       NonNull(face_name),
2034		       char_width));
2035
2036#define WideXftPattern \
2037		XFT_FAMILY, XftTypeString, "mono", \
2038		XFT_SIZE, XftTypeDouble, face_size, \
2039		XFT_SPACING, XftTypeInteger, XFT_MONO
2040
2041		if (face_name && (pat = XftNameParse(face_name)) != 0) {
2042#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
2043		    XftPatternBuild(pat,
2044				    WideXftPattern,
2045				    XFT_CHAR_WIDTH, XftTypeInteger, char_width,
2046#ifdef FC_ASPECT
2047				    FC_ASPECT, XftTypeDouble, aspect,
2048#endif
2049				    (void *) 0);
2050		    wnorm = OPEN_XFT("wide");
2051
2052		    if (wnorm != 0) {
2053			XftPatternBuild(pat,
2054					WideXftPattern,
2055					BoldXftPattern(wnorm),
2056					(void *) 0);
2057			wbold = OPEN_XFT("wide-bold");
2058
2059#if OPT_ISO_COLORS
2060			if (screen->italicULMode
2061			    && (pat = XftNameParse(face_name)) != 0) {
2062			    XftPatternBuild(pat,
2063					    WideXftPattern,
2064					    ItalXftPattern(wnorm),
2065					    (void *) 0);
2066			    wital = OPEN_XFT("wide-italic");
2067			}
2068#endif
2069#undef OPEN_XFT
2070		    }
2071		    XftPatternDestroy(pat);
2072		}
2073
2074		CACHE_XFT(screen->renderWideNorm, wnorm);
2075		CACHE_XFT(screen->renderWideBold, wbold);
2076		CACHE_XFT(screen->renderWideItal, wital);
2077	    }
2078#endif /* OPT_RENDERWIDE */
2079	}
2080	if (norm == 0) {
2081	    TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum));
2082	    xw->work.render_font = False;
2083	    update_font_renderfont();
2084	    /* now we will fall through into the bitmap fonts */
2085	} else {
2086	    setRenderFontsize(screen, win, norm, NULL);
2087	    setRenderFontsize(screen, win, bold, "bold");
2088	    setRenderFontsize(screen, win, ital, "ital");
2089#if OPT_BOX_CHARS
2090	    setupPackedFonts(xw);
2091
2092	    if (screen->force_packed) {
2093		XTermXftFonts *use = &(screen->renderFontNorm[fontnum]);
2094		SetFontHeight(screen, win, use->font->ascent + use->font->descent);
2095		SetFontWidth(screen, win, use->map.min_width);
2096		TRACE(("...packed TrueType font %dx%d vs %d\n",
2097		       win->f_height,
2098		       win->f_width,
2099		       use->map.max_width));
2100	    }
2101#endif
2102	    DUMP_XFT(xw, &(screen->renderFontNorm[fontnum]));
2103	}
2104    }
2105    /*
2106     * Are we handling a bitmap font?
2107     */
2108    else
2109#endif /* OPT_RENDERFONT */
2110    {
2111	if (is_double_width_font(font) && !(screen->fnt_prop)) {
2112	    SetFontWidth(screen, win, font->min_bounds.width);
2113	} else {
2114	    SetFontWidth(screen, win, font->max_bounds.width);
2115	}
2116	SetFontHeight(screen, win, font->ascent + font->descent);
2117	win->f_ascent = font->ascent;
2118	win->f_descent = font->descent;
2119    }
2120    i = 2 * screen->border + sbwidth;
2121    j = 2 * screen->border;
2122    width = MaxCols(screen) * win->f_width + i;
2123    height = MaxRows(screen) * win->f_height + j;
2124    win->fullwidth = (Dimension) width;
2125    win->fullheight = (Dimension) height;
2126    win->width = width - i;
2127    win->height = height - j;
2128
2129    TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
2130	   win->height,
2131	   win->width,
2132	   win->fullheight,
2133	   win->fullwidth,
2134	   win->f_height,
2135	   win->f_width,
2136	   win->f_ascent,
2137	   win->f_descent));
2138
2139    checkFontInfo(win->f_height, "height");
2140    checkFontInfo(win->f_width, "width");
2141}
2142
2143/* save this information as a side-effect for double-sized characters */
2144void
2145xtermSaveFontInfo(TScreen * screen, XFontStruct * font)
2146{
2147    screen->fnt_wide = (Dimension) (font->max_bounds.width);
2148    screen->fnt_high = (Dimension) (font->ascent + font->descent);
2149    TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
2150}
2151
2152/*
2153 * After loading a new font, update the structures that use its size.
2154 */
2155void
2156xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
2157{
2158    TScreen *screen = TScreenOf(xw);
2159
2160    int scrollbar_width;
2161    VTwin *win = &(screen->fullVwin);
2162
2163    scrollbar_width = (xw->misc.scrollbar
2164		       ? (screen->scrollWidget->core.width +
2165			  BorderWidth(screen->scrollWidget))
2166		       : 0);
2167    xtermComputeFontInfo(xw, win, screen->fnts[fNorm].fs, scrollbar_width);
2168    xtermSaveFontInfo(screen, screen->fnts[fNorm].fs);
2169
2170    if (doresize) {
2171	if (VWindow(screen)) {
2172	    xtermClear(xw);
2173	}
2174	TRACE(("xtermUpdateFontInfo {{\n"));
2175	DoResizeScreen(xw);	/* set to the new natural size */
2176	ResizeScrollBar(xw);
2177	Redraw();
2178	TRACE(("... }} xtermUpdateFontInfo\n"));
2179#ifdef SCROLLBAR_RIGHT
2180	updateRightScrollbar(xw);
2181#endif
2182    }
2183    xtermSetCursorBox(screen);
2184}
2185
2186#if OPT_BOX_CHARS
2187
2188/*
2189 * Returns true if the given character is missing from the specified font.
2190 */
2191Bool
2192xtermMissingChar(unsigned ch, XTermFonts * font)
2193{
2194    Bool result = False;
2195    XFontStruct *fs = font->fs;
2196    static XCharStruct dft, *tmp = &dft, *pc = 0;
2197
2198    if (fs->max_byte1 == 0) {
2199#if OPT_WIDE_CHARS
2200	if (ch > 255) {
2201	    TRACE(("xtermMissingChar %#04x (row)\n", ch));
2202	    return True;
2203	}
2204#endif
2205	CI_GET_CHAR_INFO_1D(fs, E2A(ch), tmp, pc);
2206    }
2207#if OPT_WIDE_CHARS
2208    else {
2209	CI_GET_CHAR_INFO_2D(fs, HI_BYTE(ch), LO_BYTE(ch), tmp, pc);
2210    }
2211#else
2212
2213    if (!pc)
2214	return False;		/* Urgh! */
2215#endif
2216
2217    if (CI_NONEXISTCHAR(pc)) {
2218	TRACE(("xtermMissingChar %#04x (!exists)\n", ch));
2219	result = True;
2220    }
2221    if (ch < 256) {
2222	font->known_missing[ch] = (Char) (result ? 2 : 1);
2223    }
2224    return result;
2225}
2226
2227/*
2228 * The grid is arbitrary, enough resolution that nothing's lost in
2229 * initialization.
2230 */
2231#define BOX_HIGH 60
2232#define BOX_WIDE 60
2233
2234#define MID_HIGH (BOX_HIGH/2)
2235#define MID_WIDE (BOX_WIDE/2)
2236
2237#define CHR_WIDE ((9*BOX_WIDE)/10)
2238#define CHR_HIGH ((9*BOX_HIGH)/10)
2239
2240/*
2241 * ...since we'll scale the values anyway.
2242 */
2243#define SCALED_X(n) ((int)(n) * (((int) font_width) - 1)) / (BOX_WIDE-1)
2244#define SCALED_Y(n) ((int)(n) * (((int) font_height) - 1)) / (BOX_HIGH-1)
2245#define SCALE_X(n) n = SCALED_X(n)
2246#define SCALE_Y(n) n = SCALED_Y(n)
2247
2248#define SEG(x0,y0,x1,y1) x0,y0, x1,y1
2249
2250/*
2251 * Draw the given graphic character, if it is simple enough (i.e., a
2252 * line-drawing character).
2253 */
2254void
2255xtermDrawBoxChar(XtermWidget xw,
2256		 unsigned ch,
2257		 unsigned flags,
2258		 GC gc,
2259		 int x,
2260		 int y,
2261		 int cells)
2262{
2263    TScreen *screen = TScreenOf(xw);
2264    /* *INDENT-OFF* */
2265    static const short glyph_ht[] = {
2266	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,5*MID_HIGH/6),	/* H */
2267	SEG(6*BOX_WIDE/10,  0,		6*BOX_WIDE/10,5*MID_HIGH/6),
2268	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
2269	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
2270	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
2271	-1
2272    }, glyph_ff[] = {
2273	SEG(1*BOX_WIDE/10,  0,		6*BOX_WIDE/10,	0),		/* F */
2274	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
2275	SEG(1*BOX_WIDE/10,  0,		0*BOX_WIDE/3, 5*MID_HIGH/6),
2276	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
2277	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
2278	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
2279	-1
2280    }, glyph_lf[] = {
2281	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,9*MID_HIGH/12),	/* L */
2282	SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
2283	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
2284	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
2285	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
2286	-1
2287    }, glyph_nl[] = {
2288	SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10,	0),		/* N */
2289	SEG(1*BOX_WIDE/10,  0,		5*BOX_WIDE/6, 5*MID_HIGH/6),
2290	SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
2291	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),	/* L */
2292	SEG(1*BOX_WIDE/3,   CHR_HIGH,	  CHR_WIDE,	CHR_HIGH),
2293	-1
2294    }, glyph_vt[] = {
2295	SEG(1*BOX_WIDE/10,   0,		5*BOX_WIDE/12,5*MID_HIGH/6),	/* V */
2296	SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
2297	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
2298	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
2299	-1
2300    }, plus_or_minus[] =
2301    {
2302	SEG(  0,	  5*BOX_HIGH/6,	  CHR_WIDE,   5*BOX_HIGH/6),
2303	SEG(  MID_WIDE,	  2*BOX_HIGH/6,	  MID_WIDE,   4*BOX_HIGH/6),
2304	SEG(  0,	  3*BOX_HIGH/6,	  CHR_WIDE,   3*BOX_HIGH/6),
2305	-1
2306    }, lower_right_corner[] =
2307    {
2308	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
2309	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
2310	-1
2311    }, upper_right_corner[] =
2312    {
2313	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
2314	SEG( MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2315	-1
2316    }, upper_left_corner[] =
2317    {
2318	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2319	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2320	-1
2321    }, lower_left_corner[] =
2322    {
2323	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
2324	SEG(  MID_WIDE,	    MID_WIDE,	  BOX_WIDE,	MID_HIGH),
2325	-1
2326    }, cross[] =
2327    {
2328	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2329	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2330	-1
2331    }, scan_line_1[] =
2332    {
2333	SEG(  0,	    0,		  BOX_WIDE,	0),
2334	-1
2335    }, scan_line_3[] =
2336    {
2337	SEG(  0,	    BOX_HIGH/4,	  BOX_WIDE,	BOX_HIGH/4),
2338	-1
2339    }, scan_line_7[] =
2340    {
2341	SEG( 0,		    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2342	-1
2343    }, scan_line_9[] =
2344    {
2345	SEG(  0,	  3*BOX_HIGH/4,	  BOX_WIDE,   3*BOX_HIGH/4),
2346	-1
2347    }, horizontal_line[] =
2348    {
2349	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	BOX_HIGH),
2350	-1
2351    }, left_tee[] =
2352    {
2353	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2354	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2355	-1
2356    }, right_tee[] =
2357    {
2358	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2359	SEG(  MID_WIDE,	    MID_HIGH,	  0,		MID_HIGH),
2360	-1
2361    }, bottom_tee[] =
2362    {
2363	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2364	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
2365	-1
2366    }, top_tee[] =
2367    {
2368	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2369	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2370	-1
2371    }, vertical_line[] =
2372    {
2373	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2374	-1
2375    }, less_than_or_equal[] =
2376    {
2377	SEG(  CHR_WIDE,	    BOX_HIGH/3,	  0,		MID_HIGH),
2378	SEG(  CHR_WIDE,	  2*BOX_HIGH/3,	  0,		MID_HIGH),
2379	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
2380	-1
2381    }, greater_than_or_equal[] =
2382    {
2383	SEG(  0,	    BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
2384	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
2385	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
2386	-1
2387    }, greek_pi[] =
2388    {
2389	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
2390	SEG(5*CHR_WIDE/6,   MID_HIGH,	5*CHR_WIDE/6,	CHR_HIGH),
2391	SEG(2*CHR_WIDE/6,   MID_HIGH,	2*CHR_WIDE/6,	CHR_HIGH),
2392	-1
2393    }, not_equal_to[] =
2394    {
2395	SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3,	CHR_HIGH),
2396	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,   2*BOX_HIGH/3),
2397	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
2398	-1
2399    };
2400    /* *INDENT-ON* */
2401
2402    static const short *lines[] =
2403    {
2404	0,			/* 00 (unused) */
2405	0,			/* 01 diamond */
2406	0,			/* 02 box */
2407	glyph_ht,		/* 03 HT */
2408	glyph_ff,		/* 04 FF */
2409	0,			/* 05 CR */
2410	glyph_lf,		/* 06 LF */
2411	0,			/* 07 degrees (small circle) */
2412	plus_or_minus,		/* 08 */
2413	glyph_nl,		/* 09 */
2414	glyph_vt,		/* 0A */
2415	lower_right_corner,	/* 0B */
2416	upper_right_corner,	/* 0C */
2417	upper_left_corner,	/* 0D */
2418	lower_left_corner,	/* 0E */
2419	cross,			/* 0F */
2420	scan_line_1,		/* 10 */
2421	scan_line_3,		/* 11 */
2422	scan_line_7,		/* 12 */
2423	scan_line_9,		/* 13 */
2424	horizontal_line,	/* 14 */
2425	left_tee,		/* 15 */
2426	right_tee,		/* 16 */
2427	bottom_tee,		/* 17 */
2428	top_tee,		/* 18 */
2429	vertical_line,		/* 19 */
2430	less_than_or_equal,	/* 1A */
2431	greater_than_or_equal,	/* 1B */
2432	greek_pi,		/* 1C */
2433	not_equal_to,		/* 1D */
2434	0,			/* 1E LB */
2435	0,			/* 1F bullet */
2436    };
2437
2438    GC gc2;
2439    CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
2440    VTwin *cgsWin = WhichVWin(screen);
2441    const short *p;
2442    unsigned font_width = (unsigned) (((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide);
2443    unsigned font_height = (unsigned) (((flags & DOUBLEHFONT) ? 2 : 1) * screen->fnt_high);
2444
2445    if (cells > 1)
2446	font_width *= (unsigned) cells;
2447
2448#if OPT_WIDE_CHARS
2449    /*
2450     * Try to show line-drawing characters if we happen to be in UTF-8
2451     * mode, but have gotten an old-style font.
2452     */
2453    if (screen->utf8_mode
2454#if OPT_RENDERFONT
2455	&& !UsingRenderFont(xw)
2456#endif
2457	&& (ch > 127)
2458	&& (ch != UCS_REPL)) {
2459	unsigned n;
2460	for (n = 1; n < 32; n++) {
2461	    if (dec2ucs(n) == ch
2462		&& !((flags & BOLD)
2463		     ? IsXtermMissingChar(screen, n, &screen->fnts[fBold])
2464		     : IsXtermMissingChar(screen, n, &screen->fnts[fNorm]))) {
2465		TRACE(("...use xterm-style linedrawing\n"));
2466		ch = n;
2467		break;
2468	    }
2469	}
2470    }
2471#endif
2472
2473    TRACE(("DRAW_BOX(%d) cell %dx%d at %d,%d%s\n",
2474	   ch, font_height, font_width, y, x,
2475	   (ch >= (sizeof(lines) / sizeof(lines[0]))
2476	    ? "-BAD"
2477	    : "")));
2478
2479    if (cgsId == gcDots) {
2480	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2481	setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2482	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2483    } else {
2484	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2485	setCgsFore(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2486	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2487    }
2488    gc2 = getCgsGC(xw, cgsWin, cgsId);
2489
2490    if (!(flags & NOBACKGROUND)) {
2491	XFillRectangle(screen->display, VDrawable(screen), gc2, x, y,
2492		       font_width,
2493		       font_height);
2494    }
2495
2496    setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2497    setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2498    setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2499    gc2 = getCgsGC(xw, cgsWin, cgsId);
2500
2501    XSetLineAttributes(screen->display, gc2,
2502		       (flags & BOLD)
2503		       ? ((font_height > 12)
2504			  ? font_height / 12
2505			  : 1)
2506		       : ((font_height > 16)
2507			  ? font_height / 16
2508			  : 1),
2509		       LineSolid,
2510		       CapProjecting,
2511		       JoinMiter);
2512
2513    if (ch == 1) {		/* diamond */
2514	XPoint points[5];
2515	int npoints = 5, n;
2516
2517	points[0].x = MID_WIDE;
2518	points[0].y = BOX_HIGH / 4;
2519
2520	points[1].x = 8 * BOX_WIDE / 8;
2521	points[1].y = MID_HIGH;
2522
2523	points[2].x = points[0].x;
2524	points[2].y = 3 * BOX_HIGH / 4;
2525
2526	points[3].x = 0 * BOX_WIDE / 8;
2527	points[3].y = points[1].y;
2528
2529	points[4].x = points[0].x;
2530	points[4].y = points[0].y;
2531
2532	for (n = 0; n < npoints; ++n) {
2533	    points[n].x = (short) SCALED_X(points[n].x);
2534	    points[n].y = (short) SCALED_Y(points[n].y);
2535	    points[n].x = (short) (points[n].x + x);
2536	    points[n].y = (short) (points[n].y + y);
2537	}
2538
2539	XFillPolygon(screen->display,
2540		     VDrawable(screen), gc2,
2541		     points, npoints,
2542		     Convex, CoordModeOrigin);
2543    } else if (ch == 7) {	/* degrees */
2544	unsigned width = (BOX_WIDE / 3);
2545	int x_coord = MID_WIDE - (int) (width / 2);
2546	int y_coord = MID_HIGH - (int) width;
2547
2548	SCALE_X(x_coord);
2549	SCALE_Y(y_coord);
2550	width = (unsigned) SCALED_X(width);
2551
2552	XDrawArc(screen->display,
2553		 VDrawable(screen), gc2,
2554		 x + x_coord, y + y_coord, width, width,
2555		 0,
2556		 360 * 64);
2557    } else if (ch == 0x1f) {	/* bullet */
2558	unsigned width = 7 * BOX_WIDE / 10;
2559	int x_coord = MID_WIDE - (int) (width / 3);
2560	int y_coord = MID_HIGH - (int) (width / 3);
2561
2562	SCALE_X(x_coord);
2563	SCALE_Y(y_coord);
2564	width = (unsigned) SCALED_X(width);
2565
2566	XDrawArc(screen->display,
2567		 VDrawable(screen), gc2,
2568		 x + x_coord, y + y_coord, width, width,
2569		 0,
2570		 360 * 64);
2571    } else if (ch < (sizeof(lines) / sizeof(lines[0]))
2572	       && (p = lines[ch]) != 0) {
2573	int coord[4];
2574	int n = 0;
2575	while (*p >= 0) {
2576	    coord[n++] = *p++;
2577	    if (n == 4) {
2578		SCALE_X(coord[0]);
2579		SCALE_Y(coord[1]);
2580		SCALE_X(coord[2]);
2581		SCALE_Y(coord[3]);
2582		XDrawLine(screen->display,
2583			  VDrawable(screen), gc2,
2584			  x + coord[0], y + coord[1],
2585			  x + coord[2], y + coord[3]);
2586		n = 0;
2587	    }
2588	}
2589    } else if (screen->force_all_chars) {
2590	/* bounding rectangle, for debugging */
2591	XDrawRectangle(screen->display, VDrawable(screen), gc2, x, y,
2592		       font_width - 1,
2593		       font_height - 1);
2594    }
2595}
2596
2597#if OPT_RENDERFONT
2598
2599/*
2600 * Check if the given character has a glyph known to Xft.
2601 *
2602 * see xc/lib/Xft/xftglyphs.c
2603 */
2604Bool
2605xtermXftMissing(XtermWidget xw, XftFont * font, unsigned wc)
2606{
2607    Bool result = False;
2608
2609    if (font != 0) {
2610	TScreen *screen = TScreenOf(xw);
2611	if (!XftGlyphExists(screen->display, font, wc)) {
2612#if OPT_WIDE_CHARS
2613	    TRACE(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n",
2614		   wc, ucs2dec(wc), dec2ucs(wc)));
2615#else
2616	    TRACE(("xtermXftMissing %d\n", wc));
2617#endif
2618	    result = True;
2619	}
2620    }
2621    return result;
2622}
2623#endif /* OPT_RENDERFONT && OPT_WIDE_CHARS */
2624
2625#endif /* OPT_BOX_CHARS */
2626
2627#if OPT_WIDE_CHARS
2628#define MY_UCS(ucs,dec) case ucs: result = dec; break
2629unsigned
2630ucs2dec(unsigned ch)
2631{
2632    unsigned result = ch;
2633    if ((ch > 127)
2634	&& (ch != UCS_REPL)) {
2635	switch (ch) {
2636	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
2637	    MY_UCS(0x25c6, 1);	/* black diamond                              */
2638	    MY_UCS(0x2592, 2);	/* medium shade                               */
2639	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
2640	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
2641	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
2642	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
2643	    MY_UCS(0x00b0, 7);	/* degree sign                                */
2644	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
2645	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
2646	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
2647	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
2648	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
2649	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
2650	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
2651	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
2652	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
2653	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
2654	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
2655	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
2656	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
2657	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
2658	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
2659	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
2660	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
2661	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
2662	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
2663	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
2664	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
2665	    MY_UCS(0x2260, 29);	/* not equal to                               */
2666	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
2667	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
2668	}
2669    }
2670    return result;
2671}
2672
2673#undef  MY_UCS
2674#define MY_UCS(ucs,dec) case dec: result = ucs; break
2675
2676unsigned
2677dec2ucs(unsigned ch)
2678{
2679    unsigned result = ch;
2680    if (xtermIsDecGraphic(ch)) {
2681	switch (ch) {
2682	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
2683	    MY_UCS(0x25c6, 1);	/* black diamond                              */
2684	    MY_UCS(0x2592, 2);	/* medium shade                               */
2685	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
2686	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
2687	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
2688	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
2689	    MY_UCS(0x00b0, 7);	/* degree sign                                */
2690	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
2691	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
2692	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
2693	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
2694	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
2695	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
2696	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
2697	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
2698	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
2699	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
2700	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
2701	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
2702	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
2703	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
2704	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
2705	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
2706	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
2707	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
2708	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
2709	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
2710	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
2711	    MY_UCS(0x2260, 29);	/* not equal to                               */
2712	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
2713	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
2714	}
2715    }
2716    return result;
2717}
2718
2719#endif /* OPT_WIDE_CHARS */
2720
2721#if OPT_SHIFT_FONTS
2722static int
2723lookupOneFontSize(XtermWidget xw, int fontnum)
2724{
2725    TScreen *screen = TScreenOf(xw);
2726
2727    if (screen->menu_font_sizes[fontnum] == 0) {
2728	XTermFonts fnt;
2729
2730	memset(&fnt, 0, sizeof(fnt));
2731	screen->menu_font_sizes[fontnum] = -1;
2732	if (xtermOpenFont(xw,
2733			  screen->MenuFontName(fontnum),
2734			  &fnt,
2735			  ((fontnum <= fontMenu_lastBuiltin)
2736			   ? fwAlways
2737			   : fwResource),
2738			  True)) {
2739	    if (fontnum <= fontMenu_lastBuiltin
2740		|| strcmp(fnt.fn, DEFFONT)) {
2741		screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
2742		if (screen->menu_font_sizes[fontnum] <= 0)
2743		    screen->menu_font_sizes[fontnum] = -1;
2744	    }
2745	    xtermCloseFont(xw, &fnt);
2746	}
2747    }
2748    return (screen->menu_font_sizes[fontnum] > 0);
2749}
2750
2751/*
2752 * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
2753 */
2754static void
2755lookupFontSizes(XtermWidget xw)
2756{
2757    int n;
2758
2759    for (n = 0; n < NMENUFONTS; n++) {
2760	(void) lookupOneFontSize(xw, n);
2761    }
2762}
2763
2764#if OPT_RENDERFONT
2765static void
2766fillInFaceSize(XtermWidget xw, int fontnum)
2767{
2768    TScreen *screen = TScreenOf(xw);
2769    float value;
2770    double face_size = xw->misc.face_size[fontnum];
2771
2772    if (face_size <= 0.0) {
2773#if OPT_SHIFT_FONTS
2774	/*
2775	 * If the user is switching font-sizes, make it follow by
2776	 * default the same ratios to the default as the fixed fonts
2777	 * would, for easy comparison.  There will be some differences
2778	 * since the fixed fonts have a variety of height/width ratios,
2779	 * but this is simpler than adding another resource value - and
2780	 * as noted above, the data for the fixed fonts are available.
2781	 */
2782	(void) lookupOneFontSize(xw, 0);
2783	if (fontnum == fontMenu_default) {
2784	    sscanf(DEFFACESIZE, "%f", &value);
2785	    face_size = value;
2786	} else if (lookupOneFontSize(xw, fontnum)
2787		   && (screen->menu_font_sizes[0]
2788		       != screen->menu_font_sizes[fontnum])) {
2789	    double ratio;
2790	    long num = screen->menu_font_sizes[fontnum];
2791	    long den = screen->menu_font_sizes[0];
2792
2793	    if (den <= 0)
2794		den = 1;
2795	    ratio = dimSquareRoot((double) num / (double) den);
2796
2797	    face_size = (ratio * xw->misc.face_size[0]);
2798	    TRACE(("scaled[%d] using %3ld/%ld = %.2f -> %f\n",
2799		   fontnum, num, den, ratio, face_size));
2800	} else
2801#endif
2802	{
2803#define LikeBitmap(s) (((s) / 78.0) * xw->misc.face_size[fontMenu_default])
2804	    switch (fontnum) {
2805	    case fontMenu_font1:
2806		face_size = LikeBitmap(2.0);
2807		break;
2808	    case fontMenu_font2:
2809		face_size = LikeBitmap(35.0);
2810		break;
2811	    case fontMenu_font3:
2812		face_size = LikeBitmap(60.0);
2813		break;
2814	    default:
2815		sscanf(DEFFACESIZE, "%f", &value);
2816		face_size = value;
2817		break;
2818	    case fontMenu_font4:
2819		face_size = LikeBitmap(90.0);
2820		break;
2821	    case fontMenu_font5:
2822		face_size = LikeBitmap(135.0);
2823		break;
2824	    case fontMenu_font6:
2825		face_size = LikeBitmap(200.0);
2826		break;
2827	    }
2828	    TRACE(("builtin[%d] -> %f\n", fontnum, face_size));
2829	}
2830	xw->misc.face_size[fontnum] = (float) face_size;
2831    }
2832}
2833
2834/* no selection or escape */
2835#define NMENU_RENDERFONTS (fontMenu_lastBuiltin + 1)
2836
2837/*
2838 * Workaround for breakage in font-packages - check if all of the bitmap font
2839 * sizes are the same, and if we're using TrueType fonts.
2840 */
2841static Boolean
2842useFaceSizes(XtermWidget xw)
2843{
2844    Boolean result = False;
2845    int n;
2846
2847    TRACE(("useFaceSizes {{\n"));
2848    if (UsingRenderFont(xw)) {
2849	Boolean nonzero = True;
2850
2851	for (n = 0; n < NMENU_RENDERFONTS; ++n) {
2852	    if (xw->misc.face_size[n] <= 0.0) {
2853		nonzero = False;
2854		break;
2855	    }
2856	}
2857	if (!nonzero) {
2858	    Boolean broken_fonts = True;
2859	    TScreen *screen = TScreenOf(xw);
2860	    long first;
2861
2862	    lookupFontSizes(xw);
2863	    first = screen->menu_font_sizes[0];
2864	    for (n = 0; n < NMENUFONTS; n++) {
2865		if (screen->menu_font_sizes[n] > 0
2866		    && screen->menu_font_sizes[n] != first) {
2867		    broken_fonts = False;
2868		    break;
2869		}
2870	    }
2871
2872	    if (broken_fonts) {
2873
2874		TRACE(("bitmap fonts are broken - set faceSize resources\n"));
2875		for (n = 0; n < NMENUFONTS; n++) {
2876		    fillInFaceSize(xw, n);
2877		}
2878
2879	    }
2880	}
2881	result = True;
2882    }
2883    TRACE(("...}}useFaceSizes %d\n", result));
2884    return result;
2885}
2886#endif /* OPT_RENDERFONT */
2887
2888/*
2889 * Find the index of a larger/smaller font (according to the sign of 'relative'
2890 * and its magnitude), starting from the 'old' index.
2891 */
2892int
2893lookupRelativeFontSize(XtermWidget xw, int old, int relative)
2894{
2895    TScreen *screen = TScreenOf(xw);
2896    int n, m = -1;
2897
2898    TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative));
2899    if (!IsIcon(screen)) {
2900#if OPT_RENDERFONT
2901	if (useFaceSizes(xw)) {
2902	    TRACE(("...using FaceSize\n"));
2903	    if (relative != 0) {
2904		for (n = 0; n < NMENU_RENDERFONTS; ++n) {
2905		    fillInFaceSize(xw, n);
2906		    if (xw->misc.face_size[n] > 0 &&
2907			xw->misc.face_size[n] != xw->misc.face_size[old]) {
2908			int cmp_0 = ((xw->misc.face_size[n] >
2909				      xw->misc.face_size[old])
2910				     ? relative
2911				     : -relative);
2912			int cmp_m = ((m < 0)
2913				     ? 1
2914				     : ((xw->misc.face_size[n] <
2915					 xw->misc.face_size[m])
2916					? relative
2917					: -relative));
2918			if (cmp_0 > 0 && cmp_m > 0) {
2919			    m = n;
2920			}
2921		    }
2922		}
2923	    }
2924	} else
2925#endif
2926	{
2927	    TRACE(("...using bitmap areas\n"));
2928	    lookupFontSizes(xw);
2929	    if (relative != 0) {
2930		for (n = 0; n < NMENUFONTS; ++n) {
2931		    if (screen->menu_font_sizes[n] > 0 &&
2932			screen->menu_font_sizes[n] !=
2933			screen->menu_font_sizes[old]) {
2934			int cmp_0 = ((screen->menu_font_sizes[n] >
2935				      screen->menu_font_sizes[old])
2936				     ? relative
2937				     : -relative);
2938			int cmp_m = ((m < 0)
2939				     ? 1
2940				     : ((screen->menu_font_sizes[n] <
2941					 screen->menu_font_sizes[m])
2942					? relative
2943					: -relative));
2944			if (cmp_0 > 0 && cmp_m > 0) {
2945			    m = n;
2946			}
2947		    }
2948		}
2949	    }
2950	}
2951	TRACE(("...new index %d\n", m));
2952	if (m >= 0) {
2953	    if (relative > 1)
2954		m = lookupRelativeFontSize(xw, m, relative - 1);
2955	    else if (relative < -1)
2956		m = lookupRelativeFontSize(xw, m, relative + 1);
2957	}
2958    }
2959    return m;
2960}
2961
2962/* ARGSUSED */
2963void
2964HandleLargerFont(Widget w GCC_UNUSED,
2965		 XEvent * event GCC_UNUSED,
2966		 String * params GCC_UNUSED,
2967		 Cardinal *param_count GCC_UNUSED)
2968{
2969    XtermWidget xw;
2970
2971    TRACE(("Handle larger-vt-font for %p\n", (void *) w));
2972    if ((xw = getXtermWidget(w)) != 0) {
2973	if (xw->misc.shift_fonts) {
2974	    TScreen *screen = TScreenOf(xw);
2975	    int m;
2976
2977	    m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
2978	    if (m >= 0) {
2979		SetVTFont(xw, m, True, NULL);
2980	    } else {
2981		Bell(xw, XkbBI_MinorError, 0);
2982	    }
2983	}
2984    }
2985}
2986
2987/* ARGSUSED */
2988void
2989HandleSmallerFont(Widget w GCC_UNUSED,
2990		  XEvent * event GCC_UNUSED,
2991		  String * params GCC_UNUSED,
2992		  Cardinal *param_count GCC_UNUSED)
2993{
2994    XtermWidget xw;
2995
2996    TRACE(("Handle smaller-vt-font for %p\n", (void *) w));
2997    if ((xw = getXtermWidget(w)) != 0) {
2998	if (xw->misc.shift_fonts) {
2999	    TScreen *screen = TScreenOf(xw);
3000	    int m;
3001
3002	    m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
3003	    if (m >= 0) {
3004		SetVTFont(xw, m, True, NULL);
3005	    } else {
3006		Bell(xw, XkbBI_MinorError, 0);
3007	    }
3008	}
3009    }
3010}
3011#endif
3012
3013int
3014xtermGetFont(const char *param)
3015{
3016    int fontnum;
3017
3018    switch (param[0]) {
3019    case 'd':
3020    case 'D':
3021    case '0':
3022	fontnum = fontMenu_default;
3023	break;
3024    case '1':
3025	fontnum = fontMenu_font1;
3026	break;
3027    case '2':
3028	fontnum = fontMenu_font2;
3029	break;
3030    case '3':
3031	fontnum = fontMenu_font3;
3032	break;
3033    case '4':
3034	fontnum = fontMenu_font4;
3035	break;
3036    case '5':
3037	fontnum = fontMenu_font5;
3038	break;
3039    case '6':
3040	fontnum = fontMenu_font6;
3041	break;
3042    case 'e':
3043    case 'E':
3044	fontnum = fontMenu_fontescape;
3045	break;
3046    case 's':
3047    case 'S':
3048	fontnum = fontMenu_fontsel;
3049	break;
3050    default:
3051	fontnum = -1;
3052	break;
3053    }
3054    return fontnum;
3055}
3056
3057/* ARGSUSED */
3058void
3059HandleSetFont(Widget w GCC_UNUSED,
3060	      XEvent * event GCC_UNUSED,
3061	      String * params,
3062	      Cardinal *param_count)
3063{
3064    XtermWidget xw;
3065
3066    if ((xw = getXtermWidget(w)) != 0) {
3067	int fontnum;
3068	VTFontNames fonts;
3069
3070	memset(&fonts, 0, sizeof(fonts));
3071
3072	if (*param_count == 0) {
3073	    fontnum = fontMenu_default;
3074	} else {
3075	    Cardinal maxparams = 1;	/* total number of params allowed */
3076	    int result = xtermGetFont(params[0]);
3077
3078	    switch (result) {
3079	    case fontMenu_default:	/* FALLTHRU */
3080	    case fontMenu_font1:	/* FALLTHRU */
3081	    case fontMenu_font2:	/* FALLTHRU */
3082	    case fontMenu_font3:	/* FALLTHRU */
3083	    case fontMenu_font4:	/* FALLTHRU */
3084	    case fontMenu_font5:	/* FALLTHRU */
3085	    case fontMenu_font6:	/* FALLTHRU */
3086		break;
3087	    case fontMenu_fontescape:
3088#if OPT_WIDE_CHARS
3089		maxparams = 5;
3090#else
3091		maxparams = 3;
3092#endif
3093		break;
3094	    case fontMenu_fontsel:
3095		maxparams = 2;
3096		break;
3097	    default:
3098		Bell(xw, XkbBI_MinorError, 0);
3099		return;
3100	    }
3101	    fontnum = result;
3102
3103	    if (*param_count > maxparams) {	/* see if extra args given */
3104		Bell(xw, XkbBI_MinorError, 0);
3105		return;
3106	    }
3107	    switch (*param_count) {	/* assign 'em */
3108#if OPT_WIDE_CHARS
3109	    case 5:
3110		fonts.f_wb = params[4];
3111		/* FALLTHRU */
3112	    case 4:
3113		fonts.f_w = params[3];
3114		/* FALLTHRU */
3115#endif
3116	    case 3:
3117		fonts.f_b = params[2];
3118		/* FALLTHRU */
3119	    case 2:
3120		fonts.f_n = params[1];
3121		break;
3122	    }
3123	}
3124
3125	SetVTFont(xw, fontnum, True, &fonts);
3126    }
3127}
3128
3129void
3130SetVTFont(XtermWidget xw,
3131	  int which,
3132	  Bool doresize,
3133	  const VTFontNames * fonts)
3134{
3135    TScreen *screen = TScreenOf(xw);
3136
3137    TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
3138	   (fonts && fonts->f_n) ? fonts->f_n : "<null>",
3139	   (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
3140
3141    if (IsIcon(screen)) {
3142	Bell(xw, XkbBI_MinorError, 0);
3143    } else if (which >= 0 && which < NMENUFONTS) {
3144	VTFontNames myfonts;
3145
3146	memset(&myfonts, 0, sizeof(myfonts));
3147	if (fonts != 0)
3148	    myfonts = *fonts;
3149
3150	if (which == fontMenu_fontsel) {	/* go get the selection */
3151	    FindFontSelection(xw, myfonts.f_n, False);
3152	} else {
3153	    int oldFont = screen->menu_font_number;
3154
3155#define USE_CACHED(field, name) \
3156	    if (myfonts.field == 0) { \
3157		myfonts.field = x_strdup(screen->menu_font_names[which][name]); \
3158		TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \
3159		       which, NonNull(myfonts.field))); \
3160	    } else { \
3161		TRACE(("set myfonts." #field " reused\n")); \
3162	    }
3163#define SAVE_FNAME(field, name) \
3164	    if (myfonts.field != 0) { \
3165		if (screen->menu_font_names[which][name] == 0 \
3166		 || strcmp(screen->menu_font_names[which][name], myfonts.field)) { \
3167		    TRACE(("updating menu_font_names[%d][" #name "] to %s\n", \
3168			   which, myfonts.field)); \
3169		    screen->menu_font_names[which][name] = x_strdup(myfonts.field); \
3170		} \
3171	    }
3172
3173	    USE_CACHED(f_n, fNorm);
3174	    USE_CACHED(f_b, fBold);
3175#if OPT_WIDE_CHARS
3176	    USE_CACHED(f_w, fWide);
3177	    USE_CACHED(f_wb, fWBold);
3178#endif
3179	    if (xtermLoadFont(xw,
3180			      &myfonts,
3181			      doresize, which)) {
3182		/*
3183		 * If successful, save the data so that a subsequent query via
3184		 * OSC-50 will return the expected values.
3185		 */
3186		SAVE_FNAME(f_n, fNorm);
3187		SAVE_FNAME(f_b, fBold);
3188#if OPT_WIDE_CHARS
3189		SAVE_FNAME(f_w, fWide);
3190		SAVE_FNAME(f_wb, fWBold);
3191#endif
3192	    } else {
3193		xtermLoadFont(xw,
3194			      xtermFontName(screen->MenuFontName(oldFont)),
3195			      doresize, oldFont);
3196		Bell(xw, XkbBI_MinorError, 0);
3197	    }
3198	}
3199    } else {
3200	Bell(xw, XkbBI_MinorError, 0);
3201    }
3202    return;
3203}
3204