fontutils.c revision 20d2c4d2
1/* $XTermId: fontutils.c,v 1.344 2010/06/15 08:18:58 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 (name == 0) {
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(char *pattern, 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, char *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(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 = 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 (name != 0) {
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    if (!xtermOpenFont(xw, myfonts.f_n, &fnts[fNorm], warn[fNorm], True))
923	goto bad;
924
925    strcpy(normal, myfonts.f_n);
926    if (!check_fontname(myfonts.f_b)) {
927	warn[fBold] = fwAlways;
928	fp = get_font_name_props(screen->display, fnts[fNorm].fs, normal);
929	if (fp != 0) {
930	    myfonts.f_b = bold_font_name(fp, fp->average_width);
931	    if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False)) {
932		myfonts.f_b = bold_font_name(fp, -1);
933		xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], fwAlways, False);
934	    }
935	    TRACE(("...derived bold %s\n", NonNull(myfonts.f_b)));
936	}
937	if (fp == 0 || fnts[fBold].fs == 0) {
938	    xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
939	    TRACE(("...cannot load a matching bold font\n"));
940	} else if (same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
941		   && got_bold_font(screen->display, fnts[fBold].fs, myfonts.f_b)) {
942	    TRACE(("...got a matching bold font\n"));
943	    cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
944	} else {
945	    xtermCloseFont(xw, &fnts[fBold]);
946	    fnts[fBold] = fnts[fNorm];
947	    TRACE(("...did not get a matching bold font\n"));
948	}
949    } else if (!xtermOpenFont(xw, myfonts.f_b, &fnts[fBold], warn[fBold], False)) {
950	xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
951	warn[fBold] = fwAlways;
952	TRACE(("...cannot load bold font %s\n", NonNull(myfonts.f_b)));
953    } else {
954	cache_menu_font_name(screen, fontnum, fBold, myfonts.f_b);
955    }
956
957    /*
958     * If there is no widefont specified, fake it by doubling AVERAGE_WIDTH
959     * of normal fonts XLFD, and asking for it.  This plucks out 18x18ja
960     * and 12x13ja as the corresponding fonts for 9x18 and 6x13.
961     */
962    if_OPT_WIDE_CHARS(screen, {
963	Bool derived;
964	char bold[MAX_FONTNAME];
965
966	if (check_fontname(myfonts.f_w)) {
967	    cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
968	} else if (!is_double_width_font(fnts[fNorm].fs)) {
969	    fp = get_font_name_props(screen->display, fnts[fNorm].fs, normal);
970	    if (fp != 0) {
971		myfonts.f_w = wide_font_name(fp);
972		warn[fWide] = fwAlways;
973		TRACE(("...derived wide %s\n", NonNull(myfonts.f_w)));
974		cache_menu_font_name(screen, fontnum, fWide, myfonts.f_w);
975	    }
976	}
977
978	if (check_fontname(myfonts.f_w)) {
979	    (void) xtermOpenFont(xw, myfonts.f_w, &fnts[fWide], warn[fWide], False);
980	} else {
981	    xtermCopyFontInfo(&fnts[fWide], &fnts[fNorm]);
982	    warn[fWide] = fwAlways;
983	}
984
985	derived = False;
986	if (!check_fontname(myfonts.f_wb)) {
987	    fp = get_font_name_props(screen->display, fnts[fBold].fs, bold);
988	    if (fp != 0) {
989		myfonts.f_wb = widebold_font_name(fp);
990		warn[fWBold] = fwAlways;
991		derived = True;
992	    }
993	}
994
995	if (check_fontname(myfonts.f_wb)) {
996
997	    xtermOpenFont(xw, myfonts.f_wb, &fnts[fWBold], warn[fWBold], False);
998
999	    if (derived
1000		&& !compatibleWideCounts(fnts[fWide].fs, fnts[fWBold].fs)) {
1001		xtermCloseFont(xw, &fnts[fWBold]);
1002	    }
1003	    if (fnts[fWBold].fs == 0) {
1004		myfonts.f_wb = myfonts.f_w;
1005		warn[fWBold] = fwAlways;
1006		xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1007		TRACE(("...cannot load wide-bold, use wide %s\n", NonNull(myfonts.f_w)));
1008	    } else {
1009		TRACE(("...%s wide/bold %s\n",
1010		       derived ? "derived" : "given",
1011		       NonNull(myfonts.f_wb)));
1012		cache_menu_font_name(screen, fontnum, fWBold, myfonts.f_wb);
1013	    }
1014	} else if (is_double_width_font(fnts[fBold].fs)) {
1015	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fBold]);
1016	    warn[fWBold] = fwAlways;
1017	    TRACE(("...bold font is double-width, use it %s\n", NonNull(myfonts.f_b)));
1018	} else {
1019	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1020	    warn[fWBold] = fwAlways;
1021	    TRACE(("...cannot load wide bold font, use wide %s\n", NonNull(myfonts.f_w)));
1022	}
1023
1024	if (EmptyFont(fnts[fWBold].fs))
1025	    goto bad;		/* can't use a 0-sized font */
1026    });
1027
1028    /*
1029     * Most of the time this call to load the font will succeed, even if
1030     * there is no wide font :  the X server doubles the width of the
1031     * normal font, or similar.
1032     *
1033     * But if it did fail for some reason, then nevermind.
1034     */
1035    if (EmptyFont(fnts[fBold].fs))
1036	goto bad;		/* can't use a 0-sized font */
1037
1038    if (!same_font_size(xw, fnts[fNorm].fs, fnts[fBold].fs)
1039	&& (is_fixed_font(fnts[fNorm].fs) && is_fixed_font(fnts[fBold].fs))) {
1040	TRACE(("...ignoring mismatched normal/bold fonts\n"));
1041	xtermCloseFont(xw, &fnts[fBold]);
1042	xtermCopyFontInfo(&fnts[fBold], &fnts[fNorm]);
1043    }
1044
1045    if_OPT_WIDE_CHARS(screen, {
1046	if (fnts[fWide].fs != 0
1047	    && fnts[fWBold].fs != 0
1048	    && !same_font_size(xw, fnts[fWide].fs, fnts[fWBold].fs)
1049	    && (is_fixed_font(fnts[fWide].fs) && is_fixed_font(fnts[fWBold].fs))) {
1050	    TRACE(("...ignoring mismatched normal/bold wide fonts\n"));
1051	    xtermCloseFont(xw, &fnts[fWBold]);
1052	    xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1053	}
1054    });
1055
1056    /*
1057     * Normal/bold fonts should be the same width.  Also, the min/max
1058     * values should be the same.
1059     */
1060    if (!is_fixed_font(fnts[fNorm].fs)
1061	|| !is_fixed_font(fnts[fBold].fs)
1062	|| fnts[fNorm].fs->max_bounds.width != fnts[fBold].fs->max_bounds.width) {
1063	TRACE(("Proportional font! normal %d/%d, bold %d/%d\n",
1064	       fnts[fNorm].fs->min_bounds.width,
1065	       fnts[fNorm].fs->max_bounds.width,
1066	       fnts[fBold].fs->min_bounds.width,
1067	       fnts[fBold].fs->max_bounds.width));
1068	proportional = True;
1069    }
1070
1071    if_OPT_WIDE_CHARS(screen, {
1072	if (fnts[fWide].fs != 0
1073	    && fnts[fWBold].fs != 0
1074	    && (!is_fixed_font(fnts[fWide].fs)
1075		|| !is_fixed_font(fnts[fWBold].fs)
1076		|| fnts[fWide].fs->max_bounds.width != fnts[fWBold].fs->max_bounds.width)) {
1077	    TRACE(("Proportional font! wide %d/%d, wide bold %d/%d\n",
1078		   fnts[fWide].fs->min_bounds.width,
1079		   fnts[fWide].fs->max_bounds.width,
1080		   fnts[fWBold].fs->min_bounds.width,
1081		   fnts[fWBold].fs->max_bounds.width));
1082	    proportional = True;
1083	}
1084    });
1085
1086    /* TODO : enforce that the width of the wide font is 2* the width
1087       of the narrow font */
1088
1089    /*
1090     * If we're switching fonts, free the old ones.  Otherwise we'll leak
1091     * the memory that is associated with the old fonts.  The
1092     * XLoadQueryFont call allocates a new XFontStruct.
1093     */
1094    xtermCloseFonts(xw, screen->fnts);
1095
1096    xtermCopyFontInfo(&(screen->fnts[fNorm]), &fnts[fNorm]);
1097    xtermCopyFontInfo(&(screen->fnts[fBold]), &fnts[fBold]);
1098#if OPT_WIDE_CHARS
1099    xtermCopyFontInfo(&(screen->fnts[fWide]), &fnts[fWide]);
1100    if (fnts[fWBold].fs == NULL)
1101	xtermCopyFontInfo(&fnts[fWBold], &fnts[fWide]);
1102    xtermCopyFontInfo(&(screen->fnts[fWBold]), &fnts[fWBold]);
1103#endif
1104
1105    new_normal = getXtermForeground(xw, xw->flags, xw->cur_foreground);
1106    new_revers = getXtermBackground(xw, xw->flags, xw->cur_background);
1107
1108    setCgsFore(xw, win, gcNorm, new_normal);
1109    setCgsBack(xw, win, gcNorm, new_revers);
1110    setCgsFont(xw, win, gcNorm, &(screen->fnts[fNorm]));
1111
1112    copyCgs(xw, win, gcBold, gcNorm);
1113    setCgsFont(xw, win, gcBold, &(screen->fnts[fBold]));
1114
1115    setCgsFore(xw, win, gcNormReverse, new_revers);
1116    setCgsBack(xw, win, gcNormReverse, new_normal);
1117    setCgsFont(xw, win, gcNormReverse, &(screen->fnts[fNorm]));
1118
1119    copyCgs(xw, win, gcBoldReverse, gcNormReverse);
1120    setCgsFont(xw, win, gcBoldReverse, &(screen->fnts[fBold]));
1121
1122    if_OPT_WIDE_CHARS(screen, {
1123	if (screen->fnts[fWide].fs != 0
1124	    && screen->fnts[fWBold].fs != 0) {
1125	    setCgsFore(xw, win, gcWide, new_normal);
1126	    setCgsBack(xw, win, gcWide, new_revers);
1127	    setCgsFont(xw, win, gcWide, &(screen->fnts[fWide]));
1128
1129	    copyCgs(xw, win, gcWBold, gcWide);
1130	    setCgsFont(xw, win, gcWBold, &(screen->fnts[fWBold]));
1131
1132	    setCgsFore(xw, win, gcWideReverse, new_revers);
1133	    setCgsBack(xw, win, gcWideReverse, new_normal);
1134	    setCgsFont(xw, win, gcWideReverse, &(screen->fnts[fWide]));
1135
1136	    copyCgs(xw, win, gcWBoldReverse, gcWideReverse);
1137	    setCgsFont(xw, win, gcWBoldReverse, &(screen->fnts[fWBold]));
1138	}
1139    });
1140
1141#if OPT_BOX_CHARS
1142    screen->allow_packing = proportional;
1143    setupPackedFonts(xw);
1144#endif
1145    screen->fnt_prop = (Boolean) (proportional && !(screen->force_packed));
1146    screen->fnt_boxes = True;
1147
1148#if OPT_BOX_CHARS
1149    /*
1150     * Xterm uses character positions 1-31 of a font for the line-drawing
1151     * characters.  Check that they are all present.  The null character
1152     * (0) is special, and is not used.
1153     */
1154#if OPT_RENDERFONT
1155    if (UsingRenderFont(xw)) {
1156	/*
1157	 * FIXME: we shouldn't even be here if we're using Xft.
1158	 */
1159	screen->fnt_boxes = False;
1160	TRACE(("assume Xft missing line-drawing chars\n"));
1161    } else
1162#endif
1163    {
1164	unsigned ch;
1165
1166	for (ch = 1; ch < 32; ch++) {
1167	    unsigned n = ch;
1168#if OPT_WIDE_CHARS
1169	    if (screen->utf8_mode || screen->unicode_font) {
1170		n = dec2ucs(ch);
1171		if (n == UCS_REPL)
1172		    continue;
1173	    }
1174#endif
1175	    if (IsXtermMissingChar(screen, n, &fnts[fNorm])) {
1176		TRACE(("missing normal char #%d\n", n));
1177		screen->fnt_boxes = False;
1178		break;
1179	    }
1180	    if (IsXtermMissingChar(screen, n, &fnts[fBold])) {
1181		TRACE(("missing bold char #%d\n", n));
1182		screen->fnt_boxes = False;
1183		break;
1184	    }
1185	}
1186    }
1187    TRACE(("Will %suse internal line-drawing characters\n",
1188	   screen->fnt_boxes ? "not " : ""));
1189#endif
1190
1191    if (screen->always_bold_mode) {
1192	screen->enbolden = screen->bold_mode;
1193    } else {
1194	screen->enbolden = screen->bold_mode
1195	    && ((fnts[fNorm].fs == fnts[fBold].fs)
1196		|| same_font_name(normal, myfonts.f_b));
1197    }
1198    TRACE(("Will %suse 1-pixel offset/overstrike to simulate bold\n",
1199	   screen->enbolden ? "" : "not "));
1200
1201    set_menu_font(False);
1202    screen->menu_font_number = fontnum;
1203    set_menu_font(True);
1204    if (tmpname) {		/* if setting escape or sel */
1205	if (screen->MenuFontName(fontnum))
1206	    free((void *) screen->MenuFontName(fontnum));
1207	screen->MenuFontName(fontnum) = tmpname;
1208	if (fontnum == fontMenu_fontescape) {
1209	    SetItemSensitivity(fontMenuEntries[fontMenu_fontescape].widget,
1210			       True);
1211	}
1212#if OPT_SHIFT_FONTS
1213	screen->menu_font_sizes[fontnum] = FontSize(fnts[fNorm].fs);
1214#endif
1215    }
1216    set_cursor_gcs(xw);
1217    xtermUpdateFontInfo(xw, doresize);
1218    TRACE(("Success Cgs - xtermLoadFont\n"));
1219    return 1;
1220
1221  bad:
1222    if (tmpname)
1223	free(tmpname);
1224    releaseWindowGCs(xw, win);
1225
1226    xtermCloseFonts(xw, fnts);
1227    TRACE(("Fail Cgs - xtermLoadFont\n"));
1228    return 0;
1229}
1230
1231#if OPT_LOAD_VTFONTS || OPT_WIDE_CHARS
1232/*
1233 * Collect font-names that we can modify with the load-vt-fonts() action.
1234 */
1235typedef struct {
1236    VTFontNames default_font;
1237    char *menu_font_names[fontMenu_lastBuiltin + 1][fMAX];
1238} SubResourceRec;
1239
1240#define MERGE_SUBFONT(src,dst,name) \
1241	if (dst.name == 0) { \
1242	    TRACE(("MERGE_SUBFONT " #dst "." #name " merge %s\n", NonNull(src.name))); \
1243	    dst.name = src.name; \
1244	} else { \
1245	    TRACE(("MERGE_SUBFONT " #dst "." #name " found %s\n", NonNull(dst.name))); \
1246	}
1247
1248#define COPY_MENU_FONTS(src,dst) \
1249	TRACE(("COPY_MENU_FONTS " #src " to " #dst "\n")); \
1250	for (n = fontMenu_default; n <= fontMenu_lastBuiltin; ++n) { \
1251	    for (m = 0; m < fMAX; ++m) { \
1252		dst.menu_font_names[n][m] = src.menu_font_names[n][m]; \
1253	    } \
1254	}
1255
1256/*
1257 * Load the "VT" font names from the given subresource name/class.  These
1258 * correspond to the VT100 resources.
1259 */
1260static Bool
1261xtermLoadVTFonts(XtermWidget xw, String myName, String myClass)
1262{
1263    static Bool initialized = False;
1264    static SubResourceRec original, referenceRec, subresourceRec;
1265
1266    /*
1267     * These are duplicates of the VT100 font resources, but with a special
1268     * application/classname passed in to distinguish them.
1269     */
1270    static XtResource font_resources[] =
1271    {
1272	Sres(XtNfont, XtCFont, default_font.f_n, DEFFONT),
1273	Sres(XtNboldFont, XtCBoldFont, default_font.f_b, DEFBOLDFONT),
1274#if OPT_WIDE_CHARS
1275	Sres(XtNwideFont, XtCWideFont, default_font.f_w, DEFWIDEFONT),
1276	Sres(XtNwideBoldFont, XtCWideBoldFont, default_font.f_wb, DEFWIDEBOLDFONT),
1277#endif
1278	Sres(XtNfont1, XtCFont1, MenuFontName(fontMenu_font1), NULL),
1279	Sres(XtNfont2, XtCFont2, MenuFontName(fontMenu_font2), NULL),
1280	Sres(XtNfont3, XtCFont3, MenuFontName(fontMenu_font3), NULL),
1281	Sres(XtNfont4, XtCFont4, MenuFontName(fontMenu_font4), NULL),
1282	Sres(XtNfont5, XtCFont5, MenuFontName(fontMenu_font5), NULL),
1283	Sres(XtNfont6, XtCFont6, MenuFontName(fontMenu_font6), NULL),
1284    };
1285    Cardinal n, m;
1286    Bool status = True;
1287    TScreen *screen = TScreenOf(xw);
1288
1289    if (!initialized) {
1290
1291	initialized = True;
1292	TRACE(("xtermLoadVTFonts saving original\n"));
1293	original.default_font = xw->misc.default_font;
1294	COPY_MENU_FONTS(xw->screen, original);
1295    }
1296
1297    if (myName == 0 || *myName == 0) {
1298	TRACE(("xtermLoadVTFonts restoring original\n"));
1299	xw->misc.default_font = original.default_font;
1300	COPY_MENU_FONTS(original, xw->screen);
1301	for (n = 0; n < XtNumber(original.menu_font_names); ++n)
1302	    screen->MenuFontName(n) = original.MenuFontName(n);
1303    } else {
1304	TRACE(("xtermLoadVTFonts(%s, %s)\n", myName, myClass));
1305
1306	memset(&subresourceRec, 0, sizeof(subresourceRec));
1307	XtGetSubresources((Widget) xw, (XtPointer) &subresourceRec,
1308			  myName, myClass,
1309			  font_resources,
1310			  (Cardinal) XtNumber(font_resources),
1311			  NULL, (Cardinal) 0);
1312
1313	if (memcmp(&referenceRec, &subresourceRec, sizeof(referenceRec))) {
1314
1315	    /*
1316	     * If a particular resource value was not found, use the original.
1317	     */
1318	    MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_n);
1319	    MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_b);
1320#if OPT_WIDE_CHARS
1321	    MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_w);
1322	    MERGE_SUBFONT(xw->misc, subresourceRec, default_font.f_wb);
1323#endif
1324	    for (n = fontMenu_font1; n <= fontMenu_lastBuiltin; ++n)
1325		MERGE_SUBFONT(xw->screen, subresourceRec, MenuFontName(n));
1326
1327	    /*
1328	     * Finally, copy the subresource data to the widget.
1329	     */
1330	    xw->misc.default_font = subresourceRec.default_font;
1331	    COPY_MENU_FONTS(subresourceRec, xw->screen);
1332	    screen->MenuFontName(fontMenu_default) = xw->misc.default_font.f_n;
1333	    screen->menu_font_names[0][fBold] = xw->misc.default_font.f_b;
1334#if OPT_WIDE_CHARS
1335	    screen->menu_font_names[0][fWide] = xw->misc.default_font.f_w;
1336	    screen->menu_font_names[0][fWBold] = xw->misc.default_font.f_wb;
1337#endif
1338	} else {
1339	    TRACE(("...no resources found\n"));
1340	    status = False;
1341	}
1342    }
1343    return status;
1344}
1345
1346#if OPT_WIDE_CHARS
1347static Bool
1348isWideFont(XFontStruct * fp, const char *tag, Bool nullOk)
1349{
1350    Bool result = False;
1351
1352    (void) tag;
1353    if (okFont(fp)) {
1354	unsigned count = countGlyphs(fp);
1355	TRACE(("isWideFont(%s) found %d cells\n", tag, count));
1356	result = (count > 256) ? True : False;
1357    } else {
1358	result = nullOk;
1359    }
1360    return result;
1361}
1362
1363/*
1364 * If the current fonts are not wide, load the UTF8 fonts.
1365 *
1366 * Called during initialization (for wide-character mode), the fonts have not
1367 * been setup, so we pass nullOk=True to isWideFont().
1368 *
1369 * Called after initialization, e.g., in response to the UTF-8 menu entry
1370 * (starting from narrow character mode), it checks if the fonts are not wide.
1371 */
1372Bool
1373xtermLoadWideFonts(XtermWidget xw, Bool nullOk)
1374{
1375    TScreen *screen = TScreenOf(xw);
1376    Bool result;
1377
1378    if (EmptyFont(screen->fnts[fWide].fs)) {
1379	result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
1380		  && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
1381    } else {
1382	result = (isWideFont(screen->fnts[fWide].fs, "wide", nullOk)
1383		  && isWideFont(screen->fnts[fWBold].fs, "wide-bold", nullOk));
1384	if (result && !screen->utf8_latin1) {
1385	    result = (isWideFont(screen->fnts[fNorm].fs, "normal", nullOk)
1386		      && isWideFont(screen->fnts[fBold].fs, "bold", nullOk));
1387	}
1388    }
1389    if (!result) {
1390	TRACE(("current fonts are not all wide%s\n", nullOk ? " nullOk" : ""));
1391	result = xtermLoadVTFonts(xw, "utf8Fonts", "Utf8Fonts");
1392    }
1393    TRACE(("xtermLoadWideFonts:%d\n", result));
1394    return result;
1395}
1396#endif /* OPT_WIDE_CHARS */
1397
1398/*
1399 * Restore the default fonts, i.e., if we had switched to wide-fonts.
1400 */
1401Bool
1402xtermLoadDefaultFonts(XtermWidget xw)
1403{
1404    Bool result;
1405    result = xtermLoadVTFonts(xw, NULL, NULL);
1406    TRACE(("xtermLoadDefaultFonts:%d\n", result));
1407    return result;
1408}
1409#endif /* OPT_LOAD_VTFONTS || OPT_WIDE_CHARS */
1410
1411#if OPT_LOAD_VTFONTS
1412void
1413HandleLoadVTFonts(Widget w,
1414		  XEvent * event GCC_UNUSED,
1415		  String * params GCC_UNUSED,
1416		  Cardinal *param_count GCC_UNUSED)
1417{
1418    static char empty[] = "";	/* appease strict compilers */
1419
1420    XtermWidget xw;
1421
1422    if ((xw = getXtermWidget(w)) != 0) {
1423	TScreen *screen = TScreenOf(xw);
1424	char buf[80];
1425	char *myName = (char *) ((*param_count > 0) ? params[0] : empty);
1426	char *convert = (char *) ((*param_count > 1) ? params[1] : myName);
1427	char *myClass = (char *) MyStackAlloc(strlen(convert), buf);
1428	int n;
1429
1430	TRACE(("HandleLoadVTFonts(%d)\n", *param_count));
1431	strcpy(myClass, convert);
1432	if (*param_count == 1)
1433	    myClass[0] = x_toupper(myClass[0]);
1434
1435	if (xtermLoadVTFonts(xw, myName, myClass)) {
1436	    /*
1437	     * When switching fonts, try to preserve the font-menu selection, since
1438	     * it is less surprising to do that (if the font-switching can be
1439	     * undone) than to switch to "Default".
1440	     */
1441	    int font_number = screen->menu_font_number;
1442	    if (font_number > fontMenu_lastBuiltin)
1443		font_number = fontMenu_lastBuiltin;
1444	    for (n = 0; n < NMENUFONTS; ++n)
1445		screen->menu_font_sizes[n] = 0;
1446	    SetVTFont(xw, font_number, True,
1447		      ((font_number == fontMenu_default)
1448		       ? &(xw->misc.default_font)
1449		       : NULL));
1450	}
1451
1452	MyStackFree(myClass, buf);
1453    }
1454}
1455#endif /* OPT_LOAD_VTFONTS */
1456
1457/*
1458 * Set the limits for the box that outlines the cursor.
1459 */
1460void
1461xtermSetCursorBox(TScreen * screen)
1462{
1463    static XPoint VTbox[NBOX];
1464    XPoint *vp;
1465    int fw = FontWidth(screen) - 1;
1466    int fh = FontHeight(screen) - 1;
1467    int hh = screen->cursor_underline ? 1 : fh;
1468
1469    vp = &VTbox[1];
1470    (vp++)->x = (short) fw;
1471    (vp++)->y = (short) hh;
1472    (vp++)->x = (short) -fw;
1473    vp->y = (short) -hh;
1474
1475    screen->box = VTbox;
1476}
1477
1478#define CACHE_XFT(dst,src) if (src != 0) {\
1479	    checkXft(xw, &(dst[fontnum]), src);\
1480	    TRACE(("Xft metrics %s[%d] = %d (%d,%d) advance %d, actual %d%s\n",\
1481		#dst,\
1482	    	fontnum,\
1483		src->height,\
1484		src->ascent,\
1485		src->descent,\
1486		src->max_advance_width,\
1487		dst[fontnum].map.min_width,\
1488		dst[fontnum].map.mixed ? " mixed" : ""));\
1489	}
1490
1491#if OPT_RENDERFONT
1492
1493#if OPT_TRACE > 1
1494static FcChar32
1495xtermXftFirstChar(XftFont * xft)
1496{
1497    FcChar32 map[FC_CHARSET_MAP_SIZE];
1498    FcChar32 next;
1499    FcChar32 first;
1500    int i;
1501
1502    first = FcCharSetFirstPage(xft->charset, map, &next);
1503    for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
1504	if (map[i]) {
1505	    FcChar32 bits = map[i];
1506	    first += i * 32;
1507	    while (!(bits & 0x1)) {
1508		bits >>= 1;
1509		first++;
1510	    }
1511	    break;
1512	}
1513    return first;
1514}
1515
1516static FcChar32
1517xtermXftLastChar(XftFont * xft)
1518{
1519    FcChar32 this, last, next;
1520    FcChar32 map[FC_CHARSET_MAP_SIZE];
1521    int i;
1522    last = FcCharSetFirstPage(xft->charset, map, &next);
1523    while ((this = FcCharSetNextPage(xft->charset, map, &next)) != FC_CHARSET_DONE)
1524	last = this;
1525    last &= ~0xff;
1526    for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
1527	if (map[i]) {
1528	    FcChar32 bits = map[i];
1529	    last += i * 32 + 31;
1530	    while (!(bits & 0x80000000)) {
1531		last--;
1532		bits <<= 1;
1533	    }
1534	    break;
1535	}
1536    return (long) last;
1537}
1538
1539static void
1540dumpXft(XtermWidget xw, XTermXftFonts * data)
1541{
1542    XftFont *xft = data->font;
1543    TScreen *screen = TScreenOf(xw);
1544    VTwin *win = WhichVWin(screen);
1545
1546    FcChar32 c;
1547    FcChar32 first = xtermXftFirstChar(xft);
1548    FcChar32 last = xtermXftLastChar(xft);
1549    unsigned count = 0;
1550    unsigned outside = 0;
1551
1552    TRACE(("dumpXft {{\n"));
1553    TRACE(("   data range %#6x..%#6x\n", first, last));
1554    for (c = first; c <= last; ++c) {
1555	if (FcCharSetHasChar(xft->charset, c)) {
1556	    int width = my_wcwidth((int) c);
1557	    XGlyphInfo extents;
1558
1559	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
1560	    TRACE(("%#6x  %2d  %.1f\n", c, width,
1561		   ((double) extents.width) / win->f_width));
1562	    if (extents.width > win->f_width)
1563		++outside;
1564	    ++count;
1565	}
1566    }
1567    TRACE(("}} %u total, %u outside\n", count, outside));
1568}
1569#define DUMP_XFT(xw, data) dumpXft(xw, data)
1570#else
1571#define DUMP_XFT(xw, data)	/* nothing */
1572#endif
1573
1574static void
1575checkXft(XtermWidget xw, XTermXftFonts * data, XftFont * xft)
1576{
1577    FcChar32 c;
1578    Dimension width = 0;
1579
1580    data->font = xft;
1581    data->map.min_width = 0;
1582    data->map.max_width = (Dimension) xft->max_advance_width;
1583
1584    /*
1585     * For each ASCII or ISO-8859-1 printable code, ask what its width is.
1586     * Given the maximum width for those, we have a reasonable estimate of
1587     * the single-column width.
1588     *
1589     * Ignore control characters - their extent information is misleading.
1590     */
1591    for (c = 32; c < 256; ++c) {
1592	if (c >= 127 && c <= 159)
1593	    continue;
1594	if (FcCharSetHasChar(xft->charset, c)) {
1595	    XGlyphInfo extents;
1596
1597	    XftTextExtents32(XtDisplay(xw), xft, &c, 1, &extents);
1598	    if (width < extents.width && extents.width <= data->map.max_width) {
1599		width = extents.width;
1600	    }
1601	}
1602    }
1603    data->map.min_width = width;
1604    data->map.mixed = (data->map.max_width >= (data->map.min_width + 1));
1605}
1606
1607static XftFont *
1608xtermOpenXft(XtermWidget xw, const char *name, XftPattern * pat, const char *tag)
1609{
1610    TScreen *screen = TScreenOf(xw);
1611    Display *dpy = screen->display;
1612    XftPattern *match;
1613    XftResult status;
1614    XftFont *result = 0;
1615
1616    if (pat != 0) {
1617	match = XftFontMatch(dpy, DefaultScreen(dpy), pat, &status);
1618	if (match != 0) {
1619	    result = XftFontOpenPattern(dpy, match);
1620	    if (result != 0) {
1621		TRACE(("...matched %s font\n", tag));
1622	    } else {
1623		TRACE(("...could did not open %s font\n", tag));
1624		XftPatternDestroy(match);
1625		if (xw->misc.fontWarnings >= fwAlways) {
1626		    TRACE(("OOPS cannot open %s font \"%s\"\n", tag, name));
1627		    fprintf(stderr, "%s: cannot open %s font \"%s\"\n",
1628			    ProgramName, tag, name);
1629		}
1630	    }
1631	} else {
1632	    TRACE(("...did not match %s font\n", tag));
1633	    if (xw->misc.fontWarnings >= fwResource) {
1634		TRACE(("OOPS: cannot match %s font \"%s\"\n", tag, name));
1635		fprintf(stderr, "%s: cannot match %s font \"%s\"\n",
1636			ProgramName, tag, name);
1637	    }
1638	}
1639    }
1640    return result;
1641}
1642#endif
1643
1644#if OPT_RENDERFONT
1645#if OPT_SHIFT_FONTS
1646/*
1647 * Don't make a dependency on the math library for a single function.
1648 * (Newton Raphson).
1649 */
1650static double
1651mySquareRoot(double value)
1652{
1653    double result = 0.0;
1654    if (value > 0.0) {
1655	int n;
1656	double older = value;
1657	for (n = 0; n < 10; ++n) {
1658	    double delta = (older * older - value) / (2.0 * older);
1659	    double newer = older - delta;
1660	    older = newer;
1661	    result = newer;
1662	    if (delta > -0.001 && delta < 0.001)
1663		break;
1664	}
1665    }
1666    return result;
1667}
1668#endif
1669
1670/*
1671 * Given the Xft font metrics, determine the actual font size.  This is used
1672 * for each font to ensure that normal, bold and italic fonts follow the same
1673 * rule.
1674 */
1675static void
1676setRenderFontsize(TScreen * screen, VTwin * win, XftFont * font, const char *tag)
1677{
1678    if (font != 0) {
1679	int width, height, ascent, descent;
1680
1681	(void) screen;
1682
1683	width = font->max_advance_width;
1684	height = font->height;
1685	ascent = font->ascent;
1686	descent = font->descent;
1687	if (height < ascent + descent) {
1688	    TRACE(("...increase height from %d\n", height));
1689	    height = ascent + descent;
1690	}
1691	if (is_double_width_font_xft(screen->display, font)) {
1692	    TRACE(("...reduced width from %d\n", width));
1693	    width >>= 1;
1694	}
1695	if (tag == 0) {
1696	    win->f_width = width;
1697	    win->f_height = height;
1698	    win->f_ascent = ascent;
1699	    win->f_descent = descent;
1700	    TRACE(("setRenderFontsize result %dx%d (%d+%d)\n",
1701		   width, height, ascent, descent));
1702	} else if (win->f_width < width ||
1703		   win->f_height < height ||
1704		   win->f_ascent < ascent ||
1705		   win->f_descent < descent) {
1706	    TRACE(("setRenderFontsize %s changed %dx%d (%d+%d) to %dx%d (%d+%d)\n",
1707		   tag,
1708		   win->f_width, win->f_height, win->f_ascent, win->f_descent,
1709		   width, height, ascent, descent));
1710
1711	    win->f_width = width;
1712	    win->f_height = height;
1713	    win->f_ascent = ascent;
1714	    win->f_descent = descent;
1715	} else {
1716	    TRACE(("setRenderFontsize %s unchanged\n", tag));
1717	}
1718    }
1719}
1720#endif
1721
1722static void
1723checkFontInfo(int value, const char *tag)
1724{
1725    if (value == 0) {
1726	fprintf(stderr,
1727		"Selected font has no non-zero %s for ISO-8859-1 encoding\n", tag);
1728	exit(1);
1729    }
1730}
1731
1732#if OPT_RENDERFONT
1733void
1734xtermCloseXft(TScreen * screen, XTermXftFonts * pub)
1735{
1736    if (pub->font != 0) {
1737	XftFontClose(screen->display, pub->font);
1738	pub->font = 0;
1739    }
1740}
1741
1742/*
1743 * Get the faceName/faceDoublesize resource setting.  Strip off "xft:", which
1744 * is not recognized by XftParseName().
1745 */
1746char *
1747getFaceName(XtermWidget xw, Bool wideName GCC_UNUSED)
1748{
1749#if OPT_RENDERWIDE
1750    char *result = (wideName
1751		    ? xw->misc.face_wide_name
1752		    : xw->misc.face_name);
1753#else
1754    char *result = xw->misc.face_name;
1755#endif
1756    if (!IsEmpty(result) && !strncmp(result, "xft:", (size_t) 4))
1757	result += 4;
1758    return x_nonempty(result);
1759}
1760
1761/*
1762 * If we change the faceName, we'll have to re-acquire all of the fonts that
1763 * are derived from it.
1764 */
1765void
1766setFaceName(XtermWidget xw, const char *value)
1767{
1768    TScreen *screen = TScreenOf(xw);
1769    int n;
1770
1771    xw->misc.face_name = x_strdup(value);
1772    for (n = 0; n < NMENUFONTS; ++n) {
1773	xw->misc.face_size[n] = -1.0;
1774	xtermCloseXft(screen, &(screen->renderFontNorm[n]));
1775	xtermCloseXft(screen, &(screen->renderFontBold[n]));
1776	xtermCloseXft(screen, &(screen->renderFontBold[n]));
1777#if OPT_RENDERWIDE
1778	xtermCloseXft(screen, &(screen->renderWideNorm[n]));
1779	xtermCloseXft(screen, &(screen->renderWideBold[n]));
1780	xtermCloseXft(screen, &(screen->renderWideItal[n]));
1781#endif
1782    }
1783}
1784#endif
1785
1786/*
1787 * Compute useful values for the font/window sizes
1788 */
1789void
1790xtermComputeFontInfo(XtermWidget xw,
1791		     VTwin * win,
1792		     XFontStruct * font,
1793		     int sbwidth)
1794{
1795    TScreen *screen = TScreenOf(xw);
1796
1797    int i, j, width, height;
1798
1799#if OPT_RENDERFONT
1800    /*
1801     * xterm contains a lot of references to fonts, assuming they are fixed
1802     * size.  This chunk of code overrides the actual font-selection (see
1803     * drawXtermText()), if the user has selected render-font.  All of the
1804     * font-loading for fixed-fonts still goes on whether or not this chunk
1805     * overrides it.
1806     */
1807    if (UsingRenderFont(xw)) {
1808	char *face_name = getFaceName(xw, False);
1809	int fontnum = screen->menu_font_number;
1810	XftFont *norm = screen->renderFontNorm[fontnum].font;
1811	XftFont *bold = screen->renderFontBold[fontnum].font;
1812	XftFont *ital = screen->renderFontItal[fontnum].font;
1813#if OPT_RENDERWIDE
1814	XftFont *wnorm = screen->renderWideNorm[fontnum].font;
1815	XftFont *wbold = screen->renderWideBold[fontnum].font;
1816	XftFont *wital = screen->renderWideItal[fontnum].font;
1817#endif
1818
1819	if (norm == 0 && face_name) {
1820	    XftPattern *pat;
1821	    double face_size = xw->misc.face_size[fontnum];
1822
1823	    TRACE(("xtermComputeFontInfo norm(face %s, size %f)\n",
1824		   face_name,
1825		   xw->misc.face_size[fontnum]));
1826
1827	    if (face_size <= 0.0) {
1828#if OPT_SHIFT_FONTS
1829		/*
1830		 * If the user is switching font-sizes, make it follow by
1831		 * default the same ratios to the default as the fixed fonts
1832		 * would, for easy comparison.  There will be some differences
1833		 * since the fixed fonts have a variety of height/width ratios,
1834		 * but this is simpler than adding another resource value - and
1835		 * as noted above, the data for the fixed fonts are available.
1836		 */
1837		lookupOneFontSize(xw, 0);
1838		lookupOneFontSize(xw, fontnum);
1839		if (fontnum == fontMenu_default) {
1840		    face_size = 14.0;
1841		} else {
1842		    double ratio;
1843		    long num = screen->menu_font_sizes[fontnum];
1844		    long den = screen->menu_font_sizes[0];
1845
1846		    if (den <= 0)
1847			den = 1;
1848		    ratio = mySquareRoot((double) num / (double) den);
1849
1850		    face_size = (ratio * xw->misc.face_size[0]);
1851		    TRACE(("scaled using %3ld/%ld = %.2f -> %f\n",
1852			   num, den, ratio, face_size));
1853		}
1854#else
1855		switch (fontnum) {
1856		case fontMenu_font1:
1857		    face_size = 8.0;
1858		    break;
1859		case fontMenu_font2:
1860		    face_size = 10.0;
1861		    break;
1862		case fontMenu_font3:
1863		    face_size = 12.0;
1864		    break;
1865		default:
1866		    face_size = 14.0;
1867		    break;
1868		case fontMenu_font4:
1869		    face_size = 16.0;
1870		    break;
1871		case fontMenu_font5:
1872		    face_size = 18.0;
1873		    break;
1874		case fontMenu_font6:
1875		    face_size = 20.0;
1876		    break;
1877		}
1878#endif
1879		xw->misc.face_size[fontnum] = (float) face_size;
1880	    }
1881
1882	    /*
1883	     * By observation (there is no documentation), XftPatternBuild is
1884	     * cumulative.  Build the bold- and italic-patterns on top of the
1885	     * normal pattern.
1886	     */
1887#define NormXftPattern \
1888	    XFT_FAMILY, XftTypeString, "mono", \
1889	    XFT_SIZE, XftTypeDouble, face_size, \
1890	    XFT_SPACING, XftTypeInteger, XFT_MONO
1891
1892#define BoldXftPattern(norm) \
1893	    XFT_WEIGHT, XftTypeInteger, XFT_WEIGHT_BOLD, \
1894	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
1895
1896#define ItalXftPattern(norm) \
1897	    XFT_SLANT, XftTypeInteger, XFT_SLANT_ITALIC, \
1898	    XFT_CHAR_WIDTH, XftTypeInteger, norm->max_advance_width
1899
1900	    if ((pat = XftNameParse(face_name)) != 0) {
1901#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
1902		XftPatternBuild(pat,
1903				NormXftPattern,
1904				(void *) 0);
1905		norm = OPEN_XFT("normal");
1906
1907		if (norm != 0) {
1908		    XftPatternBuild(pat,
1909				    BoldXftPattern(norm),
1910				    (void *) 0);
1911		    bold = OPEN_XFT("bold");
1912
1913#if OPT_ISO_COLORS
1914		    if (screen->italicULMode
1915			&& (pat = XftNameParse(face_name)) != 0) {
1916			XftPatternBuild(pat,
1917					NormXftPattern,
1918					ItalXftPattern(norm),
1919					(void *) 0);
1920			ital = OPEN_XFT("italic");
1921		    }
1922#endif /* OPT_ISO_COLORS */
1923#undef OPEN_XFT
1924
1925		    /*
1926		     * FIXME:  just assume that the corresponding font has no
1927		     * graphics characters.
1928		     */
1929		    if (screen->fnt_boxes) {
1930			screen->fnt_boxes = False;
1931			TRACE(("Xft opened - will %suse internal line-drawing characters\n",
1932			       screen->fnt_boxes ? "not " : ""));
1933		    }
1934		}
1935
1936		XftPatternDestroy(pat);
1937	    }
1938
1939	    CACHE_XFT(screen->renderFontNorm, norm);
1940	    CACHE_XFT(screen->renderFontBold, bold);
1941	    CACHE_XFT(screen->renderFontItal, ital);
1942
1943	    /*
1944	     * See xtermXftDrawString().
1945	     */
1946#if OPT_RENDERWIDE
1947	    if (norm != 0 && screen->wide_chars) {
1948		int char_width = norm->max_advance_width * 2;
1949#ifdef FC_ASPECT
1950		double aspect = ((xw->misc.face_wide_name
1951				  || screen->renderFontNorm[fontnum].map.mixed)
1952				 ? 1.0
1953				 : 2.0);
1954#endif
1955
1956		face_name = getFaceName(xw, True);
1957		TRACE(("xtermComputeFontInfo wide(face %s, char_width %d)\n",
1958		       NonNull(face_name),
1959		       char_width));
1960
1961#define WideXftPattern \
1962		XFT_FAMILY, XftTypeString, "mono", \
1963		XFT_SIZE, XftTypeDouble, face_size, \
1964		XFT_SPACING, XftTypeInteger, XFT_MONO
1965
1966		if (face_name && (pat = XftNameParse(face_name)) != 0) {
1967#define OPEN_XFT(tag) xtermOpenXft(xw, face_name, pat, tag)
1968		    XftPatternBuild(pat,
1969				    WideXftPattern,
1970				    XFT_CHAR_WIDTH, XftTypeInteger, char_width,
1971#ifdef FC_ASPECT
1972				    FC_ASPECT, XftTypeDouble, aspect,
1973#endif
1974				    (void *) 0);
1975		    wnorm = OPEN_XFT("wide");
1976
1977		    if (wnorm != 0) {
1978			XftPatternBuild(pat,
1979					WideXftPattern,
1980					BoldXftPattern(wnorm),
1981					(void *) 0);
1982			wbold = OPEN_XFT("wide-bold");
1983
1984#if OPT_ISO_COLORS
1985			if (screen->italicULMode
1986			    && (pat = XftNameParse(face_name)) != 0) {
1987			    XftPatternBuild(pat,
1988					    WideXftPattern,
1989					    ItalXftPattern(wnorm),
1990					    (void *) 0);
1991			    wital = OPEN_XFT("wide-italic");
1992			}
1993#endif
1994#undef OPEN_XFT
1995		    }
1996		    XftPatternDestroy(pat);
1997		}
1998
1999		CACHE_XFT(screen->renderWideNorm, wnorm);
2000		CACHE_XFT(screen->renderWideBold, wbold);
2001		CACHE_XFT(screen->renderWideItal, wital);
2002	    }
2003#endif /* OPT_RENDERWIDE */
2004	}
2005	if (norm == 0) {
2006	    TRACE(("...no TrueType font found for number %d, disable menu entry\n", fontnum));
2007	    xw->misc.render_font = False;
2008	    update_font_renderfont();
2009	    /* now we will fall through into the bitmap fonts */
2010	} else {
2011	    setRenderFontsize(screen, win, norm, NULL);
2012	    setRenderFontsize(screen, win, bold, "bold");
2013	    setRenderFontsize(screen, win, ital, "ital");
2014#if OPT_BOX_CHARS
2015	    setupPackedFonts(xw);
2016
2017	    if (screen->force_packed) {
2018		XTermXftFonts *use = &(screen->renderFontNorm[fontnum]);
2019		win->f_height = use->font->ascent + use->font->descent;
2020		win->f_width = use->map.min_width;
2021		TRACE(("...packed TrueType font %dx%d vs %d\n",
2022		       win->f_height,
2023		       win->f_width,
2024		       use->map.max_width));
2025	    }
2026#endif
2027	    DUMP_XFT(xw, &(screen->renderFontNorm[fontnum]));
2028	}
2029    }
2030    /*
2031     * Are we handling a bitmap font?
2032     */
2033    if (!UsingRenderFont(xw))
2034#endif /* OPT_RENDERFONT */
2035    {
2036	if (is_double_width_font(font) && !(screen->fnt_prop)) {
2037	    win->f_width = (font->min_bounds.width);
2038	} else {
2039	    win->f_width = (font->max_bounds.width);
2040	}
2041	win->f_height = (font->ascent + font->descent);
2042	win->f_ascent = font->ascent;
2043	win->f_descent = font->descent;
2044    }
2045    i = 2 * screen->border + sbwidth;
2046    j = 2 * screen->border;
2047    width = MaxCols(screen) * win->f_width + i;
2048    height = MaxRows(screen) * win->f_height + j;
2049    win->fullwidth = (Dimension) width;
2050    win->fullheight = (Dimension) height;
2051    win->width = width - i;
2052    win->height = height - j;
2053
2054    TRACE(("xtermComputeFontInfo window %dx%d (full %dx%d), fontsize %dx%d (asc %d, dsc %d)\n",
2055	   win->height,
2056	   win->width,
2057	   win->fullheight,
2058	   win->fullwidth,
2059	   win->f_height,
2060	   win->f_width,
2061	   win->f_ascent,
2062	   win->f_descent));
2063
2064    checkFontInfo(win->f_height, "height");
2065    checkFontInfo(win->f_width, "width");
2066}
2067
2068/* save this information as a side-effect for double-sized characters */
2069void
2070xtermSaveFontInfo(TScreen * screen, XFontStruct * font)
2071{
2072    screen->fnt_wide = (Dimension) (font->max_bounds.width);
2073    screen->fnt_high = (Dimension) (font->ascent + font->descent);
2074    TRACE(("xtermSaveFontInfo %dx%d\n", screen->fnt_high, screen->fnt_wide));
2075}
2076
2077/*
2078 * After loading a new font, update the structures that use its size.
2079 */
2080void
2081xtermUpdateFontInfo(XtermWidget xw, Bool doresize)
2082{
2083    TScreen *screen = TScreenOf(xw);
2084
2085    int scrollbar_width;
2086    VTwin *win = &(screen->fullVwin);
2087
2088    scrollbar_width = (xw->misc.scrollbar
2089		       ? (screen->scrollWidget->core.width +
2090			  BorderWidth(screen->scrollWidget))
2091		       : 0);
2092    xtermComputeFontInfo(xw, win, screen->fnts[fNorm].fs, scrollbar_width);
2093    xtermSaveFontInfo(screen, screen->fnts[fNorm].fs);
2094
2095    if (doresize) {
2096	if (VWindow(screen)) {
2097	    xtermClear(xw);
2098	}
2099	TRACE(("xtermUpdateFontInfo {{\n"));
2100	DoResizeScreen(xw);	/* set to the new natural size */
2101	ResizeScrollBar(xw);
2102	Redraw();
2103	TRACE(("... }} xtermUpdateFontInfo\n"));
2104#ifdef SCROLLBAR_RIGHT
2105	updateRightScrollbar(xw);
2106#endif
2107    }
2108    xtermSetCursorBox(screen);
2109}
2110
2111#if OPT_BOX_CHARS
2112
2113/*
2114 * Returns true if the given character is missing from the specified font.
2115 */
2116Bool
2117xtermMissingChar(unsigned ch, XTermFonts * font)
2118{
2119    Bool result = False;
2120    XFontStruct *fs = font->fs;
2121    static XCharStruct dft, *tmp = &dft, *pc = 0;
2122
2123    if (fs->max_byte1 == 0) {
2124#if OPT_WIDE_CHARS
2125	if (ch > 255) {
2126	    TRACE(("xtermMissingChar %#04x (row)\n", ch));
2127	    return True;
2128	}
2129#endif
2130	CI_GET_CHAR_INFO_1D(fs, E2A(ch), tmp, pc);
2131    }
2132#if OPT_WIDE_CHARS
2133    else {
2134	CI_GET_CHAR_INFO_2D(fs, HI_BYTE(ch), LO_BYTE(ch), tmp, pc);
2135    }
2136#else
2137
2138    if (!pc)
2139	return False;		/* Urgh! */
2140#endif
2141
2142    if (CI_NONEXISTCHAR(pc)) {
2143	TRACE(("xtermMissingChar %#04x (!exists)\n", ch));
2144	result = True;
2145    }
2146    if (ch < 256) {
2147	font->known_missing[ch] = (Char) (result ? 2 : 1);
2148    }
2149    return result;
2150}
2151
2152/*
2153 * The grid is arbitrary, enough resolution that nothing's lost in
2154 * initialization.
2155 */
2156#define BOX_HIGH 60
2157#define BOX_WIDE 60
2158
2159#define MID_HIGH (BOX_HIGH/2)
2160#define MID_WIDE (BOX_WIDE/2)
2161
2162#define CHR_WIDE ((9*BOX_WIDE)/10)
2163#define CHR_HIGH ((9*BOX_HIGH)/10)
2164
2165/*
2166 * ...since we'll scale the values anyway.
2167 */
2168#define SCALE_X(n) n = (n * (((int) font_width) - 1)) / (BOX_WIDE-1)
2169#define SCALE_Y(n) n = (n * (((int) font_height) - 1)) / (BOX_HIGH-1)
2170
2171#define SEG(x0,y0,x1,y1) x0,y0, x1,y1
2172
2173/*
2174 * Draw the given graphic character, if it is simple enough (i.e., a
2175 * line-drawing character).
2176 */
2177void
2178xtermDrawBoxChar(XtermWidget xw,
2179		 unsigned ch,
2180		 unsigned flags,
2181		 GC gc,
2182		 int x,
2183		 int y,
2184		 int cells)
2185{
2186    TScreen *screen = TScreenOf(xw);
2187    /* *INDENT-OFF* */
2188    static const short glyph_ht[] = {
2189	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,5*MID_HIGH/6),	/* H */
2190	SEG(6*BOX_WIDE/10,  0,		6*BOX_WIDE/10,5*MID_HIGH/6),
2191	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*BOX_WIDE/10,5*MID_HIGH/12),
2192	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
2193	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
2194	-1
2195    }, glyph_ff[] = {
2196	SEG(1*BOX_WIDE/10,  0,		6*BOX_WIDE/10,	0),		/* F */
2197	SEG(1*BOX_WIDE/10,5*MID_HIGH/12,6*CHR_WIDE/12,5*MID_HIGH/12),
2198	SEG(1*BOX_WIDE/10,  0,		0*BOX_WIDE/3, 5*MID_HIGH/6),
2199	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
2200	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
2201	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
2202	-1
2203    }, glyph_lf[] = {
2204	SEG(1*BOX_WIDE/10,  0,		1*BOX_WIDE/10,9*MID_HIGH/12),	/* L */
2205	SEG(1*BOX_WIDE/10,9*MID_HIGH/12,6*BOX_WIDE/10,9*MID_HIGH/12),
2206	SEG(1*BOX_WIDE/3,   MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* F */
2207	SEG(1*BOX_WIDE/3, 8*MID_HIGH/6,10*CHR_WIDE/12,8*MID_HIGH/6),
2208	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),
2209	-1
2210    }, glyph_nl[] = {
2211	SEG(1*BOX_WIDE/10,5*MID_HIGH/6, 1*BOX_WIDE/10,	0),		/* N */
2212	SEG(1*BOX_WIDE/10,  0,		5*BOX_WIDE/6, 5*MID_HIGH/6),
2213	SEG(5*BOX_WIDE/6, 5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
2214	SEG(1*BOX_WIDE/3,   MID_HIGH,	1*BOX_WIDE/3,	CHR_HIGH),	/* L */
2215	SEG(1*BOX_WIDE/3,   CHR_HIGH,	  CHR_WIDE,	CHR_HIGH),
2216	-1
2217    }, glyph_vt[] = {
2218	SEG(1*BOX_WIDE/10,   0,		5*BOX_WIDE/12,5*MID_HIGH/6),	/* V */
2219	SEG(5*BOX_WIDE/12,5*MID_HIGH/6, 5*BOX_WIDE/6,	0),
2220	SEG(2*BOX_WIDE/10,  MID_HIGH,	  CHR_WIDE,	MID_HIGH),	/* T */
2221	SEG(6*BOX_WIDE/10,  MID_HIGH,	6*BOX_WIDE/10,	CHR_HIGH),
2222	-1
2223    }, plus_or_minus[] =
2224    {
2225	SEG(  0,	  5*BOX_HIGH/6,	  CHR_WIDE,   5*BOX_HIGH/6),
2226	SEG(  MID_WIDE,	  2*BOX_HIGH/6,	  MID_WIDE,   4*BOX_HIGH/6),
2227	SEG(  0,	  3*BOX_HIGH/6,	  CHR_WIDE,   3*BOX_HIGH/6),
2228	-1
2229    }, lower_right_corner[] =
2230    {
2231	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
2232	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	0),
2233	-1
2234    }, upper_right_corner[] =
2235    {
2236	SEG(  0,	    MID_HIGH,	  MID_WIDE,	MID_HIGH),
2237	SEG( MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2238	-1
2239    }, upper_left_corner[] =
2240    {
2241	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2242	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2243	-1
2244    }, lower_left_corner[] =
2245    {
2246	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
2247	SEG(  MID_WIDE,	    MID_WIDE,	  BOX_WIDE,	MID_HIGH),
2248	-1
2249    }, cross[] =
2250    {
2251	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2252	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2253	-1
2254    }, scan_line_1[] =
2255    {
2256	SEG(  0,	    0,		  BOX_WIDE,	0),
2257	-1
2258    }, scan_line_3[] =
2259    {
2260	SEG(  0,	    BOX_HIGH/4,	  BOX_WIDE,	BOX_HIGH/4),
2261	-1
2262    }, scan_line_7[] =
2263    {
2264	SEG( 0,		    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2265	-1
2266    }, scan_line_9[] =
2267    {
2268	SEG(  0,	  3*BOX_HIGH/4,	  BOX_WIDE,   3*BOX_HIGH/4),
2269	-1
2270    }, horizontal_line[] =
2271    {
2272	SEG(  0,	    BOX_HIGH,	  BOX_WIDE,	BOX_HIGH),
2273	-1
2274    }, left_tee[] =
2275    {
2276	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2277	SEG(  MID_WIDE,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2278	-1
2279    }, right_tee[] =
2280    {
2281	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2282	SEG(  MID_WIDE,	    MID_HIGH,	  0,		MID_HIGH),
2283	-1
2284    }, bottom_tee[] =
2285    {
2286	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2287	SEG(  MID_WIDE,	    0,		  MID_WIDE,	MID_HIGH),
2288	-1
2289    }, top_tee[] =
2290    {
2291	SEG(  0,	    MID_HIGH,	  BOX_WIDE,	MID_HIGH),
2292	SEG(  MID_WIDE,	    MID_HIGH,	  MID_WIDE,	BOX_HIGH),
2293	-1
2294    }, vertical_line[] =
2295    {
2296	SEG(  MID_WIDE,	    0,		  MID_WIDE,	BOX_HIGH),
2297	-1
2298    }, less_than_or_equal[] =
2299    {
2300	SEG(  CHR_WIDE,	    BOX_HIGH/3,	  0,		MID_HIGH),
2301	SEG(  CHR_WIDE,	  2*BOX_HIGH/3,	  0,		MID_HIGH),
2302	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
2303	-1
2304    }, greater_than_or_equal[] =
2305    {
2306	SEG(  0,	    BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
2307	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,	MID_HIGH),
2308	SEG(  0,	  3*BOX_HIGH/4,	  CHR_WIDE,   3*BOX_HIGH/4),
2309	-1
2310    }, greek_pi[] =
2311    {
2312	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
2313	SEG(5*CHR_WIDE/6,   MID_HIGH,	5*CHR_WIDE/6,	CHR_HIGH),
2314	SEG(2*CHR_WIDE/6,   MID_HIGH,	2*CHR_WIDE/6,	CHR_HIGH),
2315	-1
2316    }, not_equal_to[] =
2317    {
2318	SEG(2*BOX_WIDE/3, 1*BOX_HIGH/3, 1*BOX_WIDE/3,	CHR_HIGH),
2319	SEG(  0,	  2*BOX_HIGH/3,	  CHR_WIDE,   2*BOX_HIGH/3),
2320	SEG(  0,	    MID_HIGH,	  CHR_WIDE,	MID_HIGH),
2321	-1
2322    };
2323    /* *INDENT-ON* */
2324
2325    static const short *lines[] =
2326    {
2327	0,			/* 00 (unused) */
2328	0,			/* 01 diamond */
2329	0,			/* 02 box */
2330	glyph_ht,		/* 03 HT */
2331	glyph_ff,		/* 04 FF */
2332	0,			/* 05 CR */
2333	glyph_lf,		/* 06 LF */
2334	0,			/* 07 degrees (small circle) */
2335	plus_or_minus,		/* 08 */
2336	glyph_nl,		/* 09 */
2337	glyph_vt,		/* 0A */
2338	lower_right_corner,	/* 0B */
2339	upper_right_corner,	/* 0C */
2340	upper_left_corner,	/* 0D */
2341	lower_left_corner,	/* 0E */
2342	cross,			/* 0F */
2343	scan_line_1,		/* 10 */
2344	scan_line_3,		/* 11 */
2345	scan_line_7,		/* 12 */
2346	scan_line_9,		/* 13 */
2347	horizontal_line,	/* 14 */
2348	left_tee,		/* 15 */
2349	right_tee,		/* 16 */
2350	bottom_tee,		/* 17 */
2351	top_tee,		/* 18 */
2352	vertical_line,		/* 19 */
2353	less_than_or_equal,	/* 1A */
2354	greater_than_or_equal,	/* 1B */
2355	greek_pi,		/* 1C */
2356	not_equal_to,		/* 1D */
2357	0,			/* 1E LB */
2358	0,			/* 1F bullet */
2359    };
2360
2361    GC gc2;
2362    CgsEnum cgsId = (ch == 2) ? gcDots : gcLine;
2363    VTwin *cgsWin = WhichVWin(screen);
2364    const short *p;
2365    unsigned font_width = (unsigned) (((flags & DOUBLEWFONT) ? 2 : 1) * screen->fnt_wide);
2366    unsigned font_height = (unsigned) (((flags & DOUBLEHFONT) ? 2 : 1) * screen->fnt_high);
2367
2368    if (cells > 1)
2369	font_width *= (unsigned) cells;
2370
2371#if OPT_WIDE_CHARS
2372    /*
2373     * Try to show line-drawing characters if we happen to be in UTF-8
2374     * mode, but have gotten an old-style font.
2375     */
2376    if (screen->utf8_mode
2377#if OPT_RENDERFONT
2378	&& !UsingRenderFont(xw)
2379#endif
2380	&& (ch > 127)
2381	&& (ch != UCS_REPL)) {
2382	unsigned n;
2383	for (n = 1; n < 32; n++) {
2384	    if (dec2ucs(n) == ch
2385		&& !((flags & BOLD)
2386		     ? IsXtermMissingChar(screen, n, &screen->fnts[fBold])
2387		     : IsXtermMissingChar(screen, n, &screen->fnts[fNorm]))) {
2388		TRACE(("...use xterm-style linedrawing\n"));
2389		ch = n;
2390		break;
2391	    }
2392	}
2393    }
2394#endif
2395
2396    TRACE(("DRAW_BOX(%d) cell %dx%d at %d,%d%s\n",
2397	   ch, font_height, font_width, y, x,
2398	   (ch >= (sizeof(lines) / sizeof(lines[0]))
2399	    ? "-BAD"
2400	    : "")));
2401
2402    if (cgsId == gcDots) {
2403	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2404	setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2405	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2406    } else {
2407	setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2408	setCgsFore(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2409	setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2410    }
2411    gc2 = getCgsGC(xw, cgsWin, cgsId);
2412
2413    if (!(flags & NOBACKGROUND)) {
2414	XFillRectangle(screen->display, VWindow(screen), gc2, x, y,
2415		       font_width,
2416		       font_height);
2417    }
2418
2419    setCgsFont(xw, cgsWin, cgsId, getCgsFont(xw, cgsWin, gc));
2420    setCgsFore(xw, cgsWin, cgsId, getCgsFore(xw, cgsWin, gc));
2421    setCgsBack(xw, cgsWin, cgsId, getCgsBack(xw, cgsWin, gc));
2422    gc2 = getCgsGC(xw, cgsWin, cgsId);
2423
2424    XSetLineAttributes(screen->display, gc2,
2425		       (flags & BOLD)
2426		       ? ((font_height > 12)
2427			  ? font_height / 12
2428			  : 1)
2429		       : ((font_height > 16)
2430			  ? font_height / 16
2431			  : 1),
2432		       LineSolid,
2433		       CapProjecting,
2434		       JoinMiter);
2435
2436    if (ch == 1) {		/* diamond */
2437	XPoint points[5];
2438	int npoints = 5, n;
2439
2440	points[0].x = MID_WIDE;
2441	points[0].y = BOX_HIGH / 4;
2442
2443	points[1].x = 8 * BOX_WIDE / 8;
2444	points[1].y = MID_HIGH;
2445
2446	points[2].x = points[0].x;
2447	points[2].y = 3 * BOX_HIGH / 4;
2448
2449	points[3].x = 0 * BOX_WIDE / 8;
2450	points[3].y = points[1].y;
2451
2452	points[4].x = points[0].x;
2453	points[4].y = points[0].y;
2454
2455	for (n = 0; n < npoints; ++n) {
2456	    SCALE_X(points[n].x);
2457	    SCALE_Y(points[n].y);
2458	    points[n].x += x;
2459	    points[n].y += y;
2460	}
2461
2462	XFillPolygon(screen->display,
2463		     VWindow(screen), gc2,
2464		     points, npoints,
2465		     Convex, CoordModeOrigin);
2466    } else if (ch == 7) {	/* degrees */
2467	unsigned width = (BOX_WIDE / 3);
2468	int x_coord = MID_WIDE - (int) (width / 2);
2469	int y_coord = MID_HIGH - (int) width;
2470
2471	SCALE_X(x_coord);
2472	SCALE_Y(y_coord);
2473	SCALE_X(width);
2474
2475	XDrawArc(screen->display,
2476		 VWindow(screen), gc2,
2477		 x + x_coord, y + y_coord, width, width,
2478		 0,
2479		 360 * 64);
2480    } else if (ch == 0x1f) {	/* bullet */
2481	unsigned width = 7 * BOX_WIDE / 10;
2482	int x_coord = MID_WIDE - (int) (width / 3);
2483	int y_coord = MID_HIGH - (int) (width / 3);
2484
2485	SCALE_X(x_coord);
2486	SCALE_Y(y_coord);
2487	SCALE_X(width);
2488
2489	XDrawArc(screen->display,
2490		 VWindow(screen), gc2,
2491		 x + x_coord, y + y_coord, width, width,
2492		 0,
2493		 360 * 64);
2494    } else if (ch < (sizeof(lines) / sizeof(lines[0]))
2495	       && (p = lines[ch]) != 0) {
2496	int coord[4];
2497	int n = 0;
2498	while (*p >= 0) {
2499	    coord[n++] = *p++;
2500	    if (n == 4) {
2501		SCALE_X(coord[0]);
2502		SCALE_Y(coord[1]);
2503		SCALE_X(coord[2]);
2504		SCALE_Y(coord[3]);
2505		XDrawLine(screen->display,
2506			  VWindow(screen), gc2,
2507			  x + coord[0], y + coord[1],
2508			  x + coord[2], y + coord[3]);
2509		n = 0;
2510	    }
2511	}
2512    } else if (screen->force_all_chars) {
2513	/* bounding rectangle, for debugging */
2514	XDrawRectangle(screen->display, VWindow(screen), gc2, x, y,
2515		       font_width - 1,
2516		       font_height - 1);
2517    }
2518}
2519
2520#if OPT_RENDERFONT
2521
2522/*
2523 * Check if the given character has a glyph known to Xft.
2524 *
2525 * see xc/lib/Xft/xftglyphs.c
2526 */
2527Bool
2528xtermXftMissing(XtermWidget xw, XftFont * font, unsigned wc)
2529{
2530    Bool result = False;
2531
2532    if (font != 0) {
2533	TScreen *screen = TScreenOf(xw);
2534	if (!XftGlyphExists(screen->display, font, wc)) {
2535#if OPT_WIDE_CHARS
2536	    TRACE(("xtermXftMissing %d (dec=%#x, ucs=%#x)\n",
2537		   wc, ucs2dec(wc), dec2ucs(wc)));
2538#else
2539	    TRACE(("xtermXftMissing %d\n", wc));
2540#endif
2541	    result = True;
2542	}
2543    }
2544    return result;
2545}
2546#endif /* OPT_RENDERFONT && OPT_WIDE_CHARS */
2547
2548#endif /* OPT_BOX_CHARS */
2549
2550#if OPT_WIDE_CHARS
2551#define MY_UCS(ucs,dec) case ucs: result = dec; break
2552unsigned
2553ucs2dec(unsigned ch)
2554{
2555    unsigned result = ch;
2556    if ((ch > 127)
2557	&& (ch != UCS_REPL)) {
2558	switch (ch) {
2559	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
2560	    MY_UCS(0x25c6, 1);	/* black diamond                              */
2561	    MY_UCS(0x2592, 2);	/* medium shade                               */
2562	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
2563	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
2564	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
2565	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
2566	    MY_UCS(0x00b0, 7);	/* degree sign                                */
2567	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
2568	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
2569	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
2570	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
2571	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
2572	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
2573	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
2574	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
2575	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
2576	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
2577	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
2578	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
2579	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
2580	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
2581	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
2582	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
2583	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
2584	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
2585	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
2586	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
2587	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
2588	    MY_UCS(0x2260, 29);	/* not equal to                               */
2589	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
2590	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
2591	}
2592    }
2593    return result;
2594}
2595
2596#undef  MY_UCS
2597#define MY_UCS(ucs,dec) case dec: result = ucs; break
2598
2599unsigned
2600dec2ucs(unsigned ch)
2601{
2602    unsigned result = ch;
2603    if (xtermIsDecGraphic(ch)) {
2604	switch (ch) {
2605	    MY_UCS(0x25ae, 0);	/* black vertical rectangle                   */
2606	    MY_UCS(0x25c6, 1);	/* black diamond                              */
2607	    MY_UCS(0x2592, 2);	/* medium shade                               */
2608	    MY_UCS(0x2409, 3);	/* symbol for horizontal tabulation           */
2609	    MY_UCS(0x240c, 4);	/* symbol for form feed                       */
2610	    MY_UCS(0x240d, 5);	/* symbol for carriage return                 */
2611	    MY_UCS(0x240a, 6);	/* symbol for line feed                       */
2612	    MY_UCS(0x00b0, 7);	/* degree sign                                */
2613	    MY_UCS(0x00b1, 8);	/* plus-minus sign                            */
2614	    MY_UCS(0x2424, 9);	/* symbol for newline                         */
2615	    MY_UCS(0x240b, 10);	/* symbol for vertical tabulation             */
2616	    MY_UCS(0x2518, 11);	/* box drawings light up and left             */
2617	    MY_UCS(0x2510, 12);	/* box drawings light down and left           */
2618	    MY_UCS(0x250c, 13);	/* box drawings light down and right          */
2619	    MY_UCS(0x2514, 14);	/* box drawings light up and right            */
2620	    MY_UCS(0x253c, 15);	/* box drawings light vertical and horizontal */
2621	    MY_UCS(0x23ba, 16);	/* box drawings scan 1                        */
2622	    MY_UCS(0x23bb, 17);	/* box drawings scan 3                        */
2623	    MY_UCS(0x2500, 18);	/* box drawings light horizontal              */
2624	    MY_UCS(0x23bc, 19);	/* box drawings scan 7                        */
2625	    MY_UCS(0x23bd, 20);	/* box drawings scan 9                        */
2626	    MY_UCS(0x251c, 21);	/* box drawings light vertical and right      */
2627	    MY_UCS(0x2524, 22);	/* box drawings light vertical and left       */
2628	    MY_UCS(0x2534, 23);	/* box drawings light up and horizontal       */
2629	    MY_UCS(0x252c, 24);	/* box drawings light down and horizontal     */
2630	    MY_UCS(0x2502, 25);	/* box drawings light vertical                */
2631	    MY_UCS(0x2264, 26);	/* less-than or equal to                      */
2632	    MY_UCS(0x2265, 27);	/* greater-than or equal to                   */
2633	    MY_UCS(0x03c0, 28);	/* greek small letter pi                      */
2634	    MY_UCS(0x2260, 29);	/* not equal to                               */
2635	    MY_UCS(0x00a3, 30);	/* pound sign                                 */
2636	    MY_UCS(0x00b7, 31);	/* middle dot                                 */
2637	}
2638    }
2639    return result;
2640}
2641
2642#endif /* OPT_WIDE_CHARS */
2643
2644#if OPT_SHIFT_FONTS
2645static void
2646lookupOneFontSize(XtermWidget xw, int fontnum)
2647{
2648    TScreen *screen = TScreenOf(xw);
2649
2650    if (screen->menu_font_sizes[fontnum] == 0) {
2651	XTermFonts fnt;
2652
2653	memset(&fnt, 0, sizeof(fnt));
2654	screen->menu_font_sizes[fontnum] = -1;
2655	if (xtermOpenFont(xw, screen->MenuFontName(fontnum), &fnt, fwAlways, True)) {
2656	    if (fontnum <= fontMenu_lastBuiltin
2657		|| strcmp(fnt.fn, DEFFONT))
2658		screen->menu_font_sizes[fontnum] = FontSize(fnt.fs);
2659	    xtermCloseFont(xw, &fnt);
2660	}
2661    }
2662}
2663
2664/*
2665 * Cache the font-sizes so subsequent larger/smaller font actions will go fast.
2666 */
2667static void
2668lookupFontSizes(XtermWidget xw)
2669{
2670    int n;
2671
2672    for (n = 0; n < NMENUFONTS; n++) {
2673	lookupOneFontSize(xw, n);
2674    }
2675}
2676
2677#if OPT_RENDERFONT
2678#define NMENU_RENDERFONTS (NMENUFONTS - 2)	/* no selection or escape */
2679static Boolean
2680useFaceSizes(XtermWidget xw)
2681{
2682    Boolean result = False;
2683    int n;
2684
2685    if (UsingRenderFont(xw)) {
2686	result = True;
2687	for (n = 0; n < NMENU_RENDERFONTS; ++n) {
2688	    if (xw->misc.face_size[n] <= 0.0) {
2689		result = False;
2690		break;
2691	    }
2692	}
2693	if (!result) {
2694	    Boolean broken_fonts = True;
2695	    TScreen *screen = TScreenOf(xw);
2696	    long first = screen->menu_font_sizes[0];
2697
2698	    lookupFontSizes(xw);
2699	    for (n = 0; n < NMENUFONTS; n++) {
2700		if (screen->menu_font_sizes[n] > 0
2701		    && screen->menu_font_sizes[n] != first) {
2702		    broken_fonts = False;
2703		    break;
2704		}
2705	    }
2706
2707	    /*
2708	     * Workaround for breakage in font-packages - check if all of the
2709	     * bitmap font sizes are the same, and if we're using TrueType
2710	     * fonts.
2711	     */
2712	    if (broken_fonts) {
2713		float lo_value = (float) 9.0e9;
2714		float hi_value = (float) 0.0;
2715		float value;
2716
2717		TRACE(("bitmap fonts are broken - set faceSize resources\n"));
2718		for (n = 0; n < NMENUFONTS; n++) {
2719		    value = xw->misc.face_size[n];
2720		    if (value > 0.0) {
2721			if (lo_value > value)
2722			    lo_value = value;
2723			if (hi_value < value)
2724			    hi_value = value;
2725		    }
2726		}
2727
2728		if (hi_value <= 0.0)
2729		    sscanf(DEFFACESIZE, "%f", &value);
2730		else
2731		    value = (float) ((hi_value + lo_value) / 2.0);
2732		if (value <= 0)
2733		    value = (float) 14.0;
2734
2735		for (n = 0; n < NMENUFONTS; n++) {
2736		    TRACE(("setting faceSize%d %.1f\n", n, value));
2737		    xw->misc.face_size[n] = value;
2738		    value = (float) (value * 1.1);
2739		}
2740		result = True;
2741	    }
2742	}
2743    }
2744    return result;
2745}
2746#endif
2747
2748/*
2749 * Find the index of a larger/smaller font (according to the sign of 'relative'
2750 * and its magnitude), starting from the 'old' index.
2751 */
2752int
2753lookupRelativeFontSize(XtermWidget xw, int old, int relative)
2754{
2755    TScreen *screen = TScreenOf(xw);
2756    int n, m = -1;
2757
2758    TRACE(("lookupRelativeFontSize(old=%d, relative=%d)\n", old, relative));
2759    if (!IsIcon(screen)) {
2760#if OPT_RENDERFONT
2761	if (useFaceSizes(xw)) {
2762	    TRACE(("...using FaceSize\n"));
2763	    if (relative != 0) {
2764		for (n = 0; n < NMENU_RENDERFONTS; ++n) {
2765		    if (xw->misc.face_size[n] > 0 &&
2766			xw->misc.face_size[n] != xw->misc.face_size[old]) {
2767			int cmp_0 = ((xw->misc.face_size[n] >
2768				      xw->misc.face_size[old])
2769				     ? relative
2770				     : -relative);
2771			int cmp_m = ((m < 0)
2772				     ? 1
2773				     : ((xw->misc.face_size[n] <
2774					 xw->misc.face_size[m])
2775					? relative
2776					: -relative));
2777			if (cmp_0 > 0 && cmp_m > 0) {
2778			    m = n;
2779			}
2780		    }
2781		}
2782	    }
2783	} else
2784#endif
2785	{
2786	    TRACE(("...using bitmap areas\n"));
2787	    lookupFontSizes(xw);
2788	    if (relative != 0) {
2789		for (n = 0; n < NMENUFONTS; ++n) {
2790		    if (screen->menu_font_sizes[n] > 0 &&
2791			screen->menu_font_sizes[n] !=
2792			screen->menu_font_sizes[old]) {
2793			int cmp_0 = ((screen->menu_font_sizes[n] >
2794				      screen->menu_font_sizes[old])
2795				     ? relative
2796				     : -relative);
2797			int cmp_m = ((m < 0)
2798				     ? 1
2799				     : ((screen->menu_font_sizes[n] <
2800					 screen->menu_font_sizes[m])
2801					? relative
2802					: -relative));
2803			if (cmp_0 > 0 && cmp_m > 0) {
2804			    m = n;
2805			}
2806		    }
2807		}
2808	    }
2809	}
2810	TRACE(("...new index %d\n", m));
2811	if (m >= 0) {
2812	    if (relative > 1)
2813		m = lookupRelativeFontSize(xw, m, relative - 1);
2814	    else if (relative < -1)
2815		m = lookupRelativeFontSize(xw, m, relative + 1);
2816	}
2817    }
2818    return m;
2819}
2820
2821/* ARGSUSED */
2822void
2823HandleLargerFont(Widget w GCC_UNUSED,
2824		 XEvent * event GCC_UNUSED,
2825		 String * params GCC_UNUSED,
2826		 Cardinal *param_count GCC_UNUSED)
2827{
2828    XtermWidget xw;
2829
2830    TRACE(("Handle larger-vt-font for %p\n", (void *) w));
2831    if ((xw = getXtermWidget(w)) != 0) {
2832	if (xw->misc.shift_fonts) {
2833	    TScreen *screen = TScreenOf(xw);
2834	    int m;
2835
2836	    m = lookupRelativeFontSize(xw, screen->menu_font_number, 1);
2837	    if (m >= 0) {
2838		SetVTFont(xw, m, True, NULL);
2839	    } else {
2840		Bell(xw, XkbBI_MinorError, 0);
2841	    }
2842	}
2843    }
2844}
2845
2846/* ARGSUSED */
2847void
2848HandleSmallerFont(Widget w GCC_UNUSED,
2849		  XEvent * event GCC_UNUSED,
2850		  String * params GCC_UNUSED,
2851		  Cardinal *param_count GCC_UNUSED)
2852{
2853    XtermWidget xw;
2854
2855    TRACE(("Handle smaller-vt-font for %p\n", (void *) w));
2856    if ((xw = getXtermWidget(w)) != 0) {
2857	if (xw->misc.shift_fonts) {
2858	    TScreen *screen = TScreenOf(xw);
2859	    int m;
2860
2861	    m = lookupRelativeFontSize(xw, screen->menu_font_number, -1);
2862	    if (m >= 0) {
2863		SetVTFont(xw, m, True, NULL);
2864	    } else {
2865		Bell(xw, XkbBI_MinorError, 0);
2866	    }
2867	}
2868    }
2869}
2870#endif
2871
2872int
2873xtermGetFont(const char *param)
2874{
2875    int fontnum;
2876
2877    switch (param[0]) {
2878    case 'd':
2879    case 'D':
2880    case '0':
2881	fontnum = fontMenu_default;
2882	break;
2883    case '1':
2884	fontnum = fontMenu_font1;
2885	break;
2886    case '2':
2887	fontnum = fontMenu_font2;
2888	break;
2889    case '3':
2890	fontnum = fontMenu_font3;
2891	break;
2892    case '4':
2893	fontnum = fontMenu_font4;
2894	break;
2895    case '5':
2896	fontnum = fontMenu_font5;
2897	break;
2898    case '6':
2899	fontnum = fontMenu_font6;
2900	break;
2901    case 'e':
2902    case 'E':
2903	fontnum = fontMenu_fontescape;
2904	break;
2905    case 's':
2906    case 'S':
2907	fontnum = fontMenu_fontsel;
2908	break;
2909    default:
2910	fontnum = -1;
2911	break;
2912    }
2913    return fontnum;
2914}
2915
2916/* ARGSUSED */
2917void
2918HandleSetFont(Widget w GCC_UNUSED,
2919	      XEvent * event GCC_UNUSED,
2920	      String * params,
2921	      Cardinal *param_count)
2922{
2923    XtermWidget xw;
2924
2925    if ((xw = getXtermWidget(w)) != 0) {
2926	int fontnum;
2927	VTFontNames fonts;
2928
2929	memset(&fonts, 0, sizeof(fonts));
2930
2931	if (*param_count == 0) {
2932	    fontnum = fontMenu_default;
2933	} else {
2934	    Cardinal maxparams = 1;	/* total number of params allowed */
2935	    int result = xtermGetFont(params[0]);
2936
2937	    switch (result) {
2938	    case fontMenu_default:	/* FALLTHRU */
2939	    case fontMenu_font1:	/* FALLTHRU */
2940	    case fontMenu_font2:	/* FALLTHRU */
2941	    case fontMenu_font3:	/* FALLTHRU */
2942	    case fontMenu_font4:	/* FALLTHRU */
2943	    case fontMenu_font5:	/* FALLTHRU */
2944	    case fontMenu_font6:	/* FALLTHRU */
2945		break;
2946	    case fontMenu_fontescape:
2947#if OPT_WIDE_CHARS
2948		maxparams = 5;
2949#else
2950		maxparams = 3;
2951#endif
2952		break;
2953	    case fontMenu_fontsel:
2954		maxparams = 2;
2955		break;
2956	    default:
2957		Bell(xw, XkbBI_MinorError, 0);
2958		return;
2959	    }
2960	    fontnum = result;
2961
2962	    if (*param_count > maxparams) {	/* see if extra args given */
2963		Bell(xw, XkbBI_MinorError, 0);
2964		return;
2965	    }
2966	    switch (*param_count) {	/* assign 'em */
2967#if OPT_WIDE_CHARS
2968	    case 5:
2969		fonts.f_wb = params[4];
2970		/* FALLTHRU */
2971	    case 4:
2972		fonts.f_w = params[3];
2973		/* FALLTHRU */
2974#endif
2975	    case 3:
2976		fonts.f_b = params[2];
2977		/* FALLTHRU */
2978	    case 2:
2979		fonts.f_n = params[1];
2980		break;
2981	    }
2982	}
2983
2984	SetVTFont(xw, fontnum, True, &fonts);
2985    }
2986}
2987
2988void
2989SetVTFont(XtermWidget xw,
2990	  int which,
2991	  Bool doresize,
2992	  const VTFontNames * fonts)
2993{
2994    TScreen *screen = TScreenOf(xw);
2995
2996    TRACE(("SetVTFont(which=%d, f_n=%s, f_b=%s)\n", which,
2997	   (fonts && fonts->f_n) ? fonts->f_n : "<null>",
2998	   (fonts && fonts->f_b) ? fonts->f_b : "<null>"));
2999
3000    if (IsIcon(screen)) {
3001	Bell(xw, XkbBI_MinorError, 0);
3002    } else if (which >= 0 && which < NMENUFONTS) {
3003	VTFontNames myfonts;
3004
3005	memset(&myfonts, 0, sizeof(myfonts));
3006	if (fonts != 0)
3007	    myfonts = *fonts;
3008
3009	if (which == fontMenu_fontsel) {	/* go get the selection */
3010	    FindFontSelection(xw, myfonts.f_n, False);
3011	} else {
3012	    int oldFont = screen->menu_font_number;
3013
3014#define USE_CACHED(field, name) \
3015	    if (myfonts.field == 0) { \
3016		myfonts.field = screen->menu_font_names[which][name]; \
3017		TRACE(("set myfonts." #field " from menu_font_names[%d][" #name "] %s\n", \
3018		       which, NonNull(myfonts.field))); \
3019	    } else { \
3020		TRACE(("set myfonts." #field " reused\n")); \
3021	    }
3022#define SAVE_FNAME(field, name) \
3023	    if (myfonts.field != 0) { \
3024		if (screen->menu_font_names[which][name] == 0 \
3025		 || strcmp(screen->menu_font_names[which][name], myfonts.field)) { \
3026		    TRACE(("updating menu_font_names[%d][" #name "] to %s\n", \
3027			   which, myfonts.field)); \
3028		    screen->menu_font_names[which][name] = x_strdup(myfonts.field); \
3029		} \
3030	    }
3031
3032	    USE_CACHED(f_n, fNorm);
3033	    USE_CACHED(f_b, fBold);
3034#if OPT_WIDE_CHARS
3035	    USE_CACHED(f_w, fWide);
3036	    USE_CACHED(f_wb, fWBold);
3037#endif
3038	    if (xtermLoadFont(xw,
3039			      &myfonts,
3040			      doresize, which)) {
3041		/*
3042		 * If successful, save the data so that a subsequent query via
3043		 * OSC-50 will return the expected values.
3044		 */
3045		SAVE_FNAME(f_n, fNorm);
3046		SAVE_FNAME(f_b, fBold);
3047#if OPT_WIDE_CHARS
3048		SAVE_FNAME(f_w, fWide);
3049		SAVE_FNAME(f_wb, fWBold);
3050#endif
3051	    } else {
3052		xtermLoadFont(xw,
3053			      xtermFontName(screen->MenuFontName(oldFont)),
3054			      doresize, oldFont);
3055		Bell(xw, XkbBI_MinorError, 0);
3056	    }
3057	}
3058    } else {
3059	Bell(xw, XkbBI_MinorError, 0);
3060    }
3061    return;
3062}
3063