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