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